Tag Archives: C#

Praktyczne różnice pomiędzy C# a Vb.Net

Od około półtora roku pracuję w projekcie, który początkowo był napisany w Vb.Net, ale teraz nowszy kod jest tworzony w C#. To świetny przykład tego, jak jeden projekt można napisać w dwóch językach, a kod w Vb.Net może odwoływać się do C# i na odwrót.

Vb.Net i C# są bardzo podobne i są kompilowane do tego samego języka pośredniego, ale niektóre różnice są bardziej zaskakujące niż inne.

Proste różnice

Zacznijmy od rzeczy oczywistych. Vb.Net używa prostego języka angielskiego w swojej składni, podczas gdy C# używa składni opartej na C. Spójrzmy na przykład.

    private string ReplaceAnd(string s)
    {
        if (s != null && s.Contains(":"))
            return s.Replace(":", "?");

        return s;
    }

A teraz ten sam przykład w Vb.Net:

    Private Function ReplaceAnd(s As String) As String
        If s IsNot Nothing And s.Contains(":") Then
            Return s.Replace(":", "?")
        End If

        Return s
    End Function

Przyzwyczajenie się do deklarowania typów za pomocą słowa kluczowego As zajęło mi trochę czasu. Także If potrzebuje słowa Then, ale to są rzeczy, które piszesz raz lub dwa razy i staje się to nawykiem.

Moim zdaniem największe zmiany przy porównaniu C # z Vb.Net to:

  • nie używamy nawiasów do bloków kodu
  • nie ma potrzeby umieszczania średników na końcu instrukcji
  • nazwy zmiennych nie uwzględniają wielkości liter
  • i kilka innych 😃

&& vs And vs AndAlso

Wszystkie te proste różnice to tylko różnice w składni języka, ale zasadniczo kod pozostaje taki sam. Jednym z moich największych błędów podczas pisania kodu Vb.Net było użycie operatora And jako && z C#. Chodzi o to, że te dwa operatory nie są równoznaczne.

Spójrzmy na przykład. Użyję dwóch metod, które sprawdzają wartość null w instrukcji If i dokonają podmiany ciągu znaków.

    Private Function ReplaceAndAlso(s As String) As String
        If s IsNot Nothing AndAlso s.Contains(":") Then
            Return s.Replace(":", "?")
        End If

        Return s
    End Function

    Private Function ReplaceAnd(s As String) As String
        If s IsNot Nothing And s.Contains(":") Then
            Return s.Replace(":", "?")
        End If

        Return s
    End Function

Różnica pomiędzy And i AndAlso jest następująca:

  • And sprawdzi warunek po prawej stronie, nawet jeśli warunek po lewej stronie jest fałszywy
  • AndAlso nie sprawdzi warunku po prawej stronie, jeśli ten po lewej jest fałszywy

Aby to zobrazować, napisałem testy jednostkowe:

    <TestCase("abc:d", "abc?d")>
    <TestCase(Nothing, Nothing)>
    Public Sub AndAlsoTest(toReplace As String, expected As String)
        ReplaceAndAlso(toReplace).Should().Be(expected)
    End Sub

    <TestCase("abc:d", "abc?d")>
    <TestCase(Nothing, Nothing)>
    Public Sub AndTest(toReplace As String, expected As String)
        ReplaceAnd(toReplace).Should().Be(expected)
    End Sub

A rezultaty są następujące:

AndTest kończy się niepowodzeniem, ponieważ operator Andsprawdza zarówno warunek po lewej, jak i po prawej stronie, co powoduje wyjątek NullReferenceException. Więc jeśli chcesz sprawdzić wartość null w instrukcji If i zrobić coś innego, po prostu użyj AndAlso.

Wymuszenie przekazania zmiennej przez wartość

Może nie jest to duża różnica, ale byłem zaskoczony, że da się to zrobić.

Przyjrzyjmy się prostemu kodowi i dwóm testom:

    <Test>
    Public Sub AddTest()
        Dim a As Integer = 5

        Dim result = Add(a)
        a.Should().Be(6)
        result.Should().Be(6)
    End Sub

    <Test>
    Public Sub AddByValueTest()
        Dim a As Integer = 5

        Dim result = Add((a))
        a.Should().Be(5)
        result.Should().Be(6)
    End Sub

    Public Function Add(ByRef x As Integer) As Integer
        x = x + 1
        Return x
    End Function

Mamy prostą metodę Add, w której zwiększamy podaną wartość o 1. Przekazujemy tę zmienną przez referencję, więc również należy ją zmienić.

Zauważ, że w AddByValueTest przekazujemy (a) i w ten sposób możemy przekazać zmienną przez wartość. Zauważ, że działa to tylko dla typów wartościowych.

A wyniki dowodzą, że tak naprawdę działa:

Zamiana enum na string

Ta różnica mnie zaskoczyła, kiedy ją zobaczyłem i była to ostateczna motywacja do napisania tego postu. Robiłem refaktoryzację wokół Vb.Net z konwersją enum-ów, a mój kolega przeglądał zadanie i powiedział.

– Hej, fajna refaktoryzacja, ale obawiam się, że może nie zadziałać. – powiedział kolega.

– Naprawdę? Uruchomiłem ten kod i działał poprawnie. – Powiedziałem.

– Lepiej sprawdź to jeszcze raz, dla pewności napisałem nawet test jednostkowy. – powiedział kolega.

Rzućmy okiem na ten test. Konwertuję enum na ciąg znaków.

    public enum ProfileType
    {
        Person = 1,
        Company = 2
    }

    [Test]
    public void ConvertEnumTest()
    {
        Convert.ToString(ProfileType.Company).Should().Be("2");
    }

A kiedy go uruchomisz, zawodzi, mój kolega miał rację. 😲

Jednak byłem podejrzliwy i kod, który napisałem był w Vb.Net, a nie w C#, więc napisałem kolejny test. Ale tym razem w Vb.Net.

    <Test>
    Public Sub ConvertEnumTest()

        Convert.ToString(ProfileType.Company).Should().Be("2")

    End Sub

I wiesz co? Przechodzi!

Więc mój oryginalny kod był poprawny. Jednak Convert.ToString() na enum działa inaczej w Vb.Net i C#.

Summary

Praca z Vb.Net brzmi jak powrót do średniowiecza, ale w rzeczywistości dość szybko przyzwyczaiłem się do składni Visual Basic. Możliwości obu są bardzo podobne, ponieważ pod spodem nadal jest ten sam kod, ale C# wydaje mi się nieco bardziej zwięzły i intuicyjny. Może dlatego, że w szkole wolałem C++ od Pascala 😁

Mam nadzieję, że post Ci się podobał, miłego dnia! 😊

Practical differences between C# vs Vb.Net

For around a year and a half, I’ve been working with a project that was initially written in Vb.Net, but now newer code is written in C#. It’s a great example of how one project can be written in two languages, and code in Vb.net can reference C# and the other way round.

Vb.Net and C# are very similar and are compiled to the same Intermediate Language, but I found some differences more surprising than the others. 

Simple differences

Let’s start with the obvious things. Vb.Net uses simple English in its syntax, while C# uses C-based syntax. Let’s have a look at the example.

    private string ReplaceAnd(string s)
    {
        if (s != null && s.Contains(":"))
            return s.Replace(":", "?");

        return s;
    }

And now the same example in Vb.Net:

    Private Function ReplaceAnd(s As String) As String
        If s IsNot Nothing And s.Contains(":") Then
            Return s.Replace(":", "?")
        End If

        Return s
    End Function

It took me some time to get used to declaring types with As keyword. Also If statement needs Then, but those are the things you write once or twice and it becomes a habit.

In my opinion, the biggest changes when comparing C# with Vb.Net are:

  • we don’t use brackets for code blocks
  • there is no need for semicolons at the end of the instruction
  • variable names are not case sensitive
  • and a few more 😃

&& vs And vs AndAlso

All those simple differences are just differences in the language, but basically, code stays the same. One of my biggest mistakes when I wrote Vb.Net code was using And as && operator in C#. The thing is that they are not the same.

Let’s have a look at the example. I’ll use two methods that check for null in the If statement and do a replace.

    Private Function ReplaceAndAlso(s As String) As String
        If s IsNot Nothing AndAlso s.Contains(":") Then
            Return s.Replace(":", "?")
        End If

        Return s
    End Function

    Private Function ReplaceAnd(s As String) As String
        If s IsNot Nothing And s.Contains(":") Then
            Return s.Replace(":", "?")
        End If

        Return s
    End Function

The difference between And and AndAlso is that:

  • And will check statement on the right side even if the statement on the left side is false
  • AndAlso will not check the right side statement if the one on the left is false

To show it clearly I wrote unit tests:

    <TestCase("abc:d", "abc?d")>
    <TestCase(Nothing, Nothing)>
    Public Sub AndAlsoTest(toReplace As String, expected As String)
        ReplaceAndAlso(toReplace).Should().Be(expected)
    End Sub

    <TestCase("abc:d", "abc?d")>
    <TestCase(Nothing, Nothing)>
    Public Sub AndTest(toReplace As String, expected As String)
        ReplaceAnd(toReplace).Should().Be(expected)
    End Sub

And the results are:

AndTest fails, cause And operator check both left and right side, which causes a NullReferenceException. So if you want to check for null in the If statement and do something else as well, just use AndAlso.

Enforce passing a variable by value

Maybe it’s not a very significant difference, but I was surprised that it can be done.

Let’s have a look at a simple code and two tests:

    <Test>
    Public Sub AddTest()
        Dim a As Integer = 5

        Dim result = Add(a)
        a.Should().Be(6)
        result.Should().Be(6)
    End Sub

    <Test>
    Public Sub AddByValueTest()
        Dim a As Integer = 5

        Dim result = Add((a))
        a.Should().Be(5)
        result.Should().Be(6)
    End Sub

    Public Function Add(ByRef x As Integer) As Integer
        x = x + 1
        Return x
    End Function

We have a simple Add method, where we increment a given value by 1. We pass this variable by reference, so it should be changed as well.

Notice that in AddByValueTest we pass (a) and this is the way how we can pass a variable by value. Note that it only works for value types.

And the results proves that it really works like that:

Converting enum to string

This difference stunned me when I saw it and it was a final motivation to write this post. I was doing a refactoring around Vb.Net with converting enums and my colleague was doing a review of the task and said.

– Hey, cool refactoring, but I’m afraid it might not work. – said the colleague.

– Really? I run that code and it was fine. – I said.

– You might want to check that again, I even wrote a unit test, to be sure. – said the colleague.

Let’s have a look at this test. I’m converting an enum to a string.

    public enum ProfileType
    {
        Person = 1,
        Company = 2
    }

    [Test]
    public void ConvertEnumTest()
    {
        Convert.ToString(ProfileType.Company).Should().Be("2");
    }

And when you run it, it fails, my colleague was right. 😲

However, I was suspicious and the code I wrote was in Vb.Net, not in C#, so I wrote another test. But this time in Vb.Net.

    <Test>
    Public Sub ConvertEnumTest()

        Convert.ToString(ProfileType.Company).Should().Be("2")

    End Sub

And you know what? It passes!

So my original code was correct. However, Convert.ToString() on an enum works differently in Vb.Net and C#.

Summary

Working with Vb.Net sounds like going back to the middle-ages, but in fact, I got accustomed to the Visual Basic syntax quite quick. The capabilities of both are very similar, cause it’s still the same code underneath, but I found C# slightly more concise and more intuitive as well. Maybe it’s because I preferred C++ over Pascal in school 😁

There are differences, as you saw in this article, but they do not interfere with your daily work.

I hope you like this post, have a good day! 😊

Returning more than one result from a method – tuple

Have you ever had a case, when you needed to return more than one parameter from a method? There are a couple of ways to cope with that and tuple might just be what you need.

The problem

Let’s look at the code, where we parse transaction data just to show it on the screen. We have a separate method for that GetTransactionData

string Data = "Michał;Breakfast;59;2021-02-13";

var value = new TupleExample().GetTransactionData(Data, out string customer);
Console.WriteLine($"Customer {customer} bought an item for {value}");

public class TupleExample
{
    public decimal GetTransactionData(string data, out string customer)
    {
        var chunks = data.Split(';');
        customer = chunks.First();

        return decimal.Parse(chunks[2]);
    }
}

 

And here is the result:

Possible solutions

There are a couple of ways we can approach this. The one that I proposed in the beginning, passing additional return values as out parameters seems to be the worst one. Nevertheless, I saw this construction more than once and there are certain cases it might be useful. For example:

var isSuccess = decimal.TryParse("59,95", out decimal result);

Another approach would be creating a new class to return the result, that might look like this:

var transactionData = new TupleExample().GetTransactionData(Data);
Console.WriteLine($"Customer {transactionData.Customer} bought an item for {transactionData.Value}");

public class TransactionData
{
    public string Customer { get; set; }
    public decimal Value { get; set; }
}

public class TupleExample
{
    public TransactionData GetTransactionData(string data)
    {
        var chunks = data.Split(';');

        return new TransactionData
        {
            Customer = chunks.First(),
            Value = decimal.Parse(chunks[2])
        };
    }
}

This solution is correct in every way. We created a class to represent what we return from a method. Properties are named correctly.
What if we could achieve the same without creating a new class?

Meet tuples

A tuple is a value type that can store a set of data elements. It is a very flexible structure, similar to dynamic types, so you can set names for your properties. Let’s finish talking and jump into code:

var transactionData = new TupleExample().GetTransactionData(Data);
Console.WriteLine($"Customer {transactionData.Customer} bought an item for {transactionData.Value}");

public class TupleExample
{
    public (string Customer, decimal Value) GetTransactionData(string data)
    {
        var chunks = data.Split(';');

        return (chunks.First(), decimal.Parse(chunks[2]));
    }
}

Have you noticed, that I haven’t changed the code that’s using a method GetTransactionData? 😲 It is the change that affects only the method itself, but it just makes it simpler and more concise.

Notice how tuple can be defined:

You can create a tuple with new keyword or with parenthesis.

var tuple = new Tuple<string, decimal>("John", 20);
(string, decimal) tuple2 = ("Anna", 30);
(string Name, decimal Value) tuple3 = ("Cathrine", 40);

Console.WriteLine($"Customer {tuple2.Item1} bought an item for {tuple2.Item2}");
Console.WriteLine($"Customer {tuple3.Name} bought an item for {tuple3.Value}");

You can specify property names, but if you don’t, you can use default names like Item1, Item2, etc.

Summary

A tuple is a simple, dynamic type when we don’t want to create a class for just a single usage. It is flexible and easy to create.

Would it be useful for you? I will give it a try 😀