برچسب: methods

  • Methods should have a coherent level of abstraction | Code4IT

    Methods should have a coherent level of abstraction | Code4IT


    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    Mixed levels of abstraction make the code harder to understand.

    At the first sight, the reader should be able to understand what the code does without worrying about the details of the operations.

    Take this code snippet as an example:

    public void PrintPriceWithDiscountForProduct(string productId)
    {
        var product = sqlRepository.FindProduct(productId);
        var withDiscount = product.Price * 0.9;
        Console.WriteLine("The final price is " + withDiscount);
    }
    

    We are mixing multiple levels of operations. In the same method, we are

    • integrating with an external service
    • performing algebraic operations
    • concatenating strings
    • printing using .NET Console class

    Some operations have a high level of abstraction (call an external service, I don’t care how) while others are very low-level (calculate the price discount using the formula ProductPrice*0.9).

    Here the readers lose focus on the overall meaning of the method because they’re distracted by the actual implementation.

    When I’m talking about abstraction, I mean how high-level an operation is: the more we stay away from bit-to-bit and mathematical operations, the more our code is abstract.

    Cleaner code should let the reader understand what’s going on without the need of understanding the details: if they’re interested in the details, they can just read the internals of the methods.

    We can improve the previous method by splitting it into smaller methods:

    public void PrintPriceWithDiscountForProduct(string productId)
    {
        var product = GetProduct(productId);
        var withDiscount = CalculateDiscountedPrice(product);
        PrintPrice(withDiscount);
    }
    
    private Product GetProduct(string productId)
    {
        return sqlRepository.FindProduct(productId);
    }
    
    private double CalculateDiscountedPrice(Product product)
    {
        return product.Price * 0.9;
    }
    
    private void PrintPrice(double price)
    {
        Console.WriteLine("The final price is " + price);
    }
    

    Here you can see the different levels of abstraction: the operations within PrintPriceWithDiscountForProduct have a coherent level of abstraction: they just tell you what the steps performed in this method are; all the methods describe an operation at a high level, without expressing the internal operations.

    Yes, now the code is much longer. But we have gained some interesting advantages:

    • readers can focus on the “what” before getting to the “how”;
    • we have more reusable code (we can reuse GetProduct, CalculateDiscountedPrice, and PrintPrice in other methods);
    • if an exception is thrown, we can easily understand where it happened, because we have more information on the stack trace.

    You can read more about the latest point here:

    🔗 Clean code tip: small functions bring smarter exceptions | Code4IT

    This article first appeared on Code4IT 🐧

    Happy coding!

    🐧



    Source link

  • pick the right methods! | Code4IT

    pick the right methods! | Code4IT


    LINQ is a set of methods that help developers perform operations on sets of items. There are tons of methods – do you know which is the one for you?

    Table of Contents

    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    LINQ is one of the most loved functionalities by C# developers. It allows you to perform calculations and projections over a collection of items, making your code easy to build and, even more, easy to understand.

    As of C# 11, there are tens of methods and overloads you can choose from. Some of them seem similar, but there are some differences that might not be obvious to C# beginners.

    In this article, we’re gonna learn the differences between couples of methods, so that you can choose the best one that fits your needs.

    First vs FirstOrDefault

    Both First and FirstOrDefault allow you to get the first item of a collection that matches some requisites passed as a parameter, usually with a Lambda expression:

    int[] numbers = new int[] { -2, 1, 6, 12 };
    
    var mod3OrDefault = numbers.FirstOrDefault(n => n % 3 == 0);
    var mod3 = numbers.First(n => n % 3 == 0);
    

    Using FirstOrDefault you get the first item that matches the condition. If no items are found you’ll get the default value for that type. The default value depends on the data type:

    Data type Default value
    int 0
    string null
    bool false
    object null

    To know the default value for a specific type, just run default(string).

    So, coming back to FirstOrDefault, we have these two possible outcomes:

    int[] numbers = new int[] { -2,  1, 6, 12 };
    numbers.FirstOrDefault(n => n % 3 == 0); // 6
    numbers.FirstOrDefault(n => n % 7 == 0); // 0
    

    On the other hand, First throws an InvalidOperationException with the message “Sequence contains no matching element” if no items in the collection match the filter criterion:

    int[] numbers = new int[] { -2,  1, 6, 12 };
    numbers.First(n => n % 3 == 0); // 6
    numbers.First(n => n % 7 == 0); // throws InvalidOperationException
    

    First vs Single

    While First returns the first item that satisfies the condition, even if there are more than two or more, Single ensures that no more than one item matches that condition.

    If there are two or more items that passing the filter, an InvalidOperationException is thrown with the message “Sequence contains more than one matching element”.

    int[] numbers = new int[] { -2, 1, 6, 12 };
    numbers.First(n => n % 3 == 0); // 6
    numbers.Single(n => n % 3 == 0); // throws exception because both 6 and 12 are accepted values
    

    Both methods have their corresponding -OrDefault counterpart: SingleOrDefault returns the default value if no items are valid.

    int[] numbers = new int[] { -2, 1, 6, 12 };
    
    numbers.SingleOrDefault(n => n % 4 == 0); // 12
    numbers.SingleOrDefault(n => n % 7 == 0); // 0, because no items are %7
    numbers.SingleOrDefault(n => n % 3 == 0); // throws exception
    

    Any vs Count

    Both Any and Count give you indications about the presence or absence of items for which the specified predicate returns True.

    int[] numbers = new int[] { -2, 1, 6, 12 };
    
    numbers.Any(n => n % 3 == 0); // true
    numbers.Count(n => n % 3 == 0); // 2
    

    the difference is that Any returns a boolean, while Count returns an integer.

    Where vs First

    As you remember, First returns only one item.

    If you need all the items that meet the specified criteria, you can use Where:

    int[] numbers = new int[] { -2, 1, 6, 12 };
    numbers.Where(n => n % 3 == 0); // [6, 12]
    

    Sort vs Order

    Both Sort and Order deal with the sorting of collections.

    The main difference is that Sort sorts the items in place, modifying the original collection.

    On the contrary, Order and OrderBy create a new collection of items with the same items of the original sequence but sorted.

    List<int> originalNumbers = new List<int> { -7, 1, 5, -6};
    originalNumbers.Sort(); // originalNumbers now is [-7, -6, 1, 5]
    

    Also, notice that Sort is valid only on List<T>, and not Arrays or generic Enumerables.

    OrderBy and Order create a brand-new collection of items.

    List<int> originalNumbers = new List<int> { -7, 1, 5, -6};
    var sortedNumbers = originalNumbers.OrderBy(n => n);
    // sortedNumbers is [-7, -6, 1, 5];
    // originalNumbers is [-7, 1, 5, -6];
    

    💡 Starting from C# 11 we can simplify OrderBy(n => n) and use Order()!

    Further readings

    C# collections do not natively expose such methods. They are ALL Extension methods (well, except for Sort, which is a native of List<T> ).

    If you want to learn what are Extension Methods and how you can write your own methods, have a look at this article:

    🔗 How you can create extension methods in C# | Code4IT

    Then, in the C# TIPS section of my blog, there are several articles that you might find interesting.

    One of these is about a LINQ method that you might want to know: SelectMany.

    🔗 C# Tip: SelectMany in LINQ

    This article first appeared on Code4IT 🐧

    If you want to learn more about Sort, the best place is the documentation:

    🔗 List.Sort Method | Microsoft Docs

    Wrapping up

    In this article, we learned the differences between couples of LINQ methods.

    Each of them has a purpose, and you should use the right one for each case.

    ❓ A question for you: talking about performance, which is more efficient: First or Single? And what about Count() == 0 vs Any()? Drop a message below if you know the answer! 📩

    I hope you enjoyed this article! Let’s keep in touch on Twitter or on LinkedIn, if you want! 🤜🤛

    Happy coding!

    🐧





    Source link