Handling C # features that have been popular over the years

Source: Internet
Author: User
Tags dotnet

Original address: Http://www.dotnetcurry.com/csharp/1411/csharp-favorite-features

In writing this article, C # has been 17 years of history, can be sure that it did not go anywhere. The C # language team is constantly working to develop new features to improve the developer experience.

In this article, I'm introducing the C # Historical version while sharing my favorite features and demonstrating the benefits while emphasizing usability.

C # 1.0

c#1.0 (ISO-1) is really a language, but nothing exciting and lacks many of the features that developers like. Think carefully, I can say that there is only one special feature- implicit and explicit interface implementations .

Interfaces are still prevalent in the process of developing C # today, taking the following Idateprovider interface as an example.

public interface IDateProvider{    DateTime GetDate();}

There's nothing special about it, and now we're going to do two implementations-the first of which is an implicit implementation, as follows:

public class DefaultDateProvider : IDateProvider{    public DateTime GetDate()    {        return DateTime.Now;    }}

The second implementation is the following explicit implementation:

public class MinDateProvider : IDateProvider{    DateTime IDateProvider.GetDate()    {        return DateTime.MinValue;    }}

Note an explicit implementation of how to omit the access modifier. In addition, the method name is written as Idateprovider.getdate (), which prefixes the interface name as a qualifier.
These two things make the call more explicit.

A good aspect of explicit interface implementations is that it forces consumers to rely on interfaces. An instance object that implements an interface explicitly must use the interface itself, and no other interface members are available!

However, when you declare it as an interface or pass the implementation as an argument to the desired interface, the members are available as expected.

This is particularly useful because it enforces the use of interfaces. By using interfaces directly, code is not coupled to the underlying implementation. Similarly, explicit interface implementations avoid naming or method signature ambiguity-and enable a single class to implement multiple interfaces with the same members.

Jeffery Richter in his CLR via C # reminds us that the two main problems of explicit interface implementations are that value type instances are boxed when projected onto an interface and explicitly implemented methods, and cannot be called by a derived class.

Keep in mind that boxing and unpacking can affect performance. In any programming, you should evaluate use cases to ensure that tools are utilized.

C # 2.0

As a reference, I will list all the features of C # 2.0 (ISO-2).

    • Anonymous methods
    • Covariance and Contravariance
    • Generic type
    • Iterators
    • Nullable types
    • Partial types

My most favorite thing about generics or the wobble between iterators is that it's a very difficult choice for me, and ultimately more like generics, and by the way, for that reason.

Because I use generics more frequently than write iterators. Many of the SOLID programming principles in C # are hardened using generics, which also helps keep the code dry. Don't get me wrong, I've also written some iterators that are worth using in C #!

Let's look at generics in more detail.

Editor's note: Learn how generics are used in C # to improve application maintainability

Generics introduce the concept of type parameters to the. NET framework, which allows classes and methods to be designed to defer one or more types of specifications until a class or method is declared and instantiated by client code.

Let's imagine that we have a class named Databag, as a packet. It may look like this:

public class DataBag{    public void Add(object data)    {        // omitted for brevity...    }            }

This seems like a great idea at first, because you can add anything to this databag instance. But when you really think about what this means, it's pretty scary.

All added content is implicitly packaged as System.Object. In addition, if a value type is added, boxing occurs. These are the performance considerations you should be aware of.

Generics solve all of this and also increase type safety. Let's modify the previous example, include a type parameter T in the class, and notice the change in the method signature.

public class DataBag{    public void Add(T data)    {        // omitted for brevity...    }}

For example, a Databag instance will now allow only callers to add a DateTime instance. To type safe, no boxing or unpacking ... Let the finer things happen.

Generic type parameters can also be constrained. Universal constraints are powerful because they must adhere to the appropriate constraints, allowing only a limited range of available type parameters.

There are several methods for writing generic type parameter constraints, consider the following syntax:

public class DataBag where T : struct { /* T 值类型 */ }public class DataBag where T : class { /* T 类、接口、委托、数组 */ }public class DataBag where T : new() { /* T 有无参构造函数 */ }public class DataBag where T : IPerson { /* T 继承 IPerson */ }public class DataBag where T : BaseClass { /* T 派生自 BaseClass */ }public class DataBag where T : U { /* T 继承 U, U 也是一个泛型参数 */ }

Multiple constraints are allowed, separated by commas. Type parameter constraints take effect immediately, that is, compilation errors prevent programmers from making mistakes. Consider the following databag constraints.

public class DataBag where T : class{    public void Add(T value)    {        // omitted for brevity...    }}

Now, if I try to instantiate the databag,c# compiler it will let me know what I did wrong. More specifically, it requires that the type ' DateTime ' must be a reference type so that it can be used as a ' T ' parameter in a generic type or ' Program.databag ' method.

C # 3.0

The following is a list of the main features of c#3.0.

    • Anonymous type
    • Auto-implemented Properties
    • Expression tree
    • Extension methods
    • Lambda expression
    • query expressions

I hover over whether to choose a lambda expression or an extension method . However, by contacting my current C # programming, I use lambda operators more than any other C # operator. I can't express my affection for it.
There are many opportunities in C # to take advantage of lambda expressions and lambda operators. The/= lambda operator isolates the input on the left from the lambda expression body on the right.

Some developers prefer to consider lambda expressions as a lengthy way to express delegate invocation. The Action, Func type is just a predefined generic delegate in the System namespace.

Let's start with a hypothetical question and use lambda expressions to help us write some expressive and concise C # code.

Imagine that we have a large number of records representing trend weather information. We may want to perform some operations on this data, rather than traversing it in a typical loop, but at some point we can take a different approach.

public class WeatherData{    public DateTime TimeStampUtc { get; set; }    public decimal Temperature { get; set; }} private IEnumerable GetWeatherByZipCode(string zipCode) { /* ... */ }

Since Getweatherbyzipcode's call returns a IEnumerable, it may seem like you want to iterate over the iteration. Suppose we have a way to calculate the mean temperature.

private static decimal CalculateAverageTemperature(    IEnumerable weather,     DateTime startUtc,     DateTime endUtc){    var sumTemp = 0m;    var total = 0;    foreach (var weatherData in weather)    {        if (weatherData.TimeStampUtc > startUtc &&            weatherData.TimeStampUtc < endUtc)        {            ++ total;            sumTemp += weatherData.Temperature;        }    }    return sumTemp / total;}

We declare some local variables to store the sum and sum of the temperatures in all the filtered date ranges so that we can calculate the mean later. Within an iteration is an if logical block that checks whether the weather data is within a specific date range. Can be rewritten as follows:

private static decimal CalculateAverageTempatureLambda(    IEnumerable weather,    DateTime startUtc,    DateTime endUtc){    return weather.Where(w => w.TimeStampUtc > startUtc &&                              w.TimeStampUtc  w.Temperature)                  .Average();}

As you can see, the code is greatly simplified. If the logical block is actually just a predicate, if the weather date is within range, we will continue to do some extra processing-like a filter. Then just like calling Average, when we need to aggregate the temperature, we only need to project (or select) The IEnumerable temperature filter list.

In the where and Select extension methods on the IEnumerable interface, use the LAMBD a expression as the parameter. The Where method requires a Func

C # 4.0

C # 4.0 has fewer key features than the previous version.

    • Dynamic binding
    • Embedded Interop Types
    • Covariance and Contravariance in generics
    • Named/optional Parameters

All of these features are very useful. But for me, I prefer to name optional parameters, not covariance and contravariance in generics. The trade-offs between the two depend on which one is most commonly used, and the feature that most recently benefited C # developers.

Named optional parameters are well deserved, although this is a very simple feature, its practicality is very high. I would like to ask, who has not written a method of overloading or with optional parameters?

When you write an optional parameter, you must provide a default value for it. If your parameter is a value type, then it must be a literal or constant value, or you can use the default keyword. Similarly, you can declare a value type as Nullable and assign it a value of NULL. Suppose we have a warehouse with a GetData method.

public class Repository{    public DataTable GetData(        string storedProcedure,        DateTime start = default(DateTime),        DateTime? end = null,        int? rows = 50,        int? offSet = null)    {        // omitted for brevity...            }}

As we can see, the parameter list for this method is quite long-fortunately there are several optional parameters. Therefore, callers can ignore them and use the default values. As you declare, we can invoke it by passing only the StoredProcedure parameter.

var repo = new Repository();var sales = repo.GetData("sp_GetHistoricalSales");

Now that we are familiar with the optional parameter characteristics and how these features work, by the way, use named parameters. In the example above, let's say we just want our data table to return 100 rows instead of the default 50 rows. We can change our call to include a named parameter and pass the desired override value.

var repo = new Repository();var sales = repo.GetData("sp_GetHistoricalSales", rows: 100);
C # 5.0

Like the c#4.0 version, there are not many features in the c#5.0 version-but one of them is very powerful.

    • Async/Wait
    • Caller information

When C # 5.0 was released, it actually changed the way that C # developers wrote asynchronous code. There's still a lot of confusion today, and I'm here to assure you that this is much simpler than most people think. This is a significant leap in C #-it introduces a language-level asynchronous model that gives developers a great deal of "async" code that looks and feels synchronous (or at least continuous).

Asynchronous programming is very powerful when dealing with I/O related, such as interacting with databases, networks, file systems, and so on. Asynchronous programming helps handle throughput by using non-blocking methods. This mechanism is used in a transparent asynchronous state machine to use the pause point and the corresponding continuation method.

Similarly, if the CPU load is computationally heavy, you might want to consider doing this asynchronously. This helps the user experience because the UI thread is not blocked, but is free to respond to other UI interactions.

Editor's note: The best practice for using asynchronous waits in C # Asynchronous programming, Http://www.dotnetcurry.com/csharp/1307/async-await-asynchronous-programming-examples. ***

In C # 5.0, asynchronous programming was simplified when the language added two new keywords async and await. These keywords apply to task and task types. The following table will be used as a reference:

The task and task types represent asynchronous operations. These operations can either return a task or return a void task. When you use the Async keyword to modify the return method, it enables the method body to use the await keyword. When the await keyword is evaluated, the control flow is returned to the caller, and the execution is paused at that point in the method. When the pending operation finishes, it resumes execution at the same time.

class IOBoundAsyncExample{    // Yes, this is the internet Chuck Norris Database of jokes!    private const string Url = "http://api.icndb.com/jokes/random?limitTo=[nerdy]";     internal async Task GetJokeAsync()    {        using (var client = new HttpClient())        {            var response = await client.GetStringAsync(Url);            var result = JsonConvert.DeserializeObject(response);             return result.Value.Joke;        }    }}public class Result{    [JsonProperty("type")] public string Type { get; set; }    [JsonProperty("value")] public Value Value { get; set; }} public class Value{    [JsonProperty("id")] public int Id { get; set; }    [JsonProperty("joke")] public string Joke { get; set; }}

We define a simple class with a method called Getjokeasync, which returns a Task when we call the method. For callers, the Getjokeasync method will eventually give you a string-or possibly an error.

Resumes execution from the paused place when the response is returned. The result JSON is then deserialized into an instance of the result class, and the Joke property is returned.

C # 6.0

C # 6.0 has a lot of great improvements and it's hard to choose my favorite features.

    • Dictionary initialization
    • Exception filter
    • Expression-Body Members
    • Nameof operator
    • Empty merge operator
    • Property class
    • Static references
    • string interpolation

I narrowed the range down to three outstanding features: string interpolation, empty merge operators, and the nameof operator.

Although the nameof operator is great and I often use it, it is clear that the other two features are more influential. It's also a dilemma, and eventually the string interpolation wins.

The empty merge operator is useful, which allows me to write less code, but not necessarily to prevent errors in my code. When you use string interpolation, you can prevent errors at run time.

string interpolation syntax in C # is enabled when you use the $ symbol to insert string literals. The equivalent of telling the C # compiler that we are going to use various C # variables, logic, or expressions to insert into this string. This is for manual stitching of strings, even string. The Format method is an important upgrade. Let's take a look at the following code:

class Person{    public string FirstName { get; set; }    public string LastName { get; set; }     public override string ToString()        => string.Format("{0} {1}", FirstName);}

We have a simple person class with two attributes representing first and last names. We use String. Format overrides the ToString method. The problem is that, at compile time, the "{0} {1}" parameter is prone to error when the developer wants to use the last name as part of the resulting string. As in the above code, they forgot to add the last name. Similarly, developers can easily exchange parameter positions, in the chaotic format the text only passes the first index, and so on ... Now consider using string interpolation implementations.

class Person{    public string FirstName { get; set; } = "David";    public string LastName { get; set; } = "Pine";    public DateTime DateOfBirth { get; set; } = new DateTime(1984, 7, 7);     public override string ToString()        => $"{FirstName} {LastName} (Born {DateOfBirth:MMMM dd, yyyy})";}

I ventured to add the dateOfBirth property and some default property values. In addition, we now use string interpolation to override the ToString method. As a developer, it is much more difficult to make these mistakes. Finally, I can also format in an interpolation expression. Note that the third interpolation, dateOfBirth is a DateTime type-so we can use all the standard formats that are customary. Just use the: operator to separate variables and formatting.

Example Output :

    • David Pine (Born July 7, 1984)

Editor's note: For more information on the new features of c#6.0, please read the http://www.dotnetcurry.com/csharp/1042/csharp-6-new-features***

C # 7.0
    • Expression-Body Members
    • Local methods
    • Out variable
    • Pattern matching
    • Local references and references returned
    • Tuple and structure

Between a pattern match, a tuple, and an out variable, I selected an out variable.
pattern matching is great, but I don't really feel like I'm using it often, at least not yet. Maybe I'll use it more in the future, but there's not much in the C # code I've written so far that can be used. Again, this is a great feature, but not my favorite C # 7.0 feature.

Tuples are also a good improvement, and this is an important part of serving the language, and being a first-class citizen is really worth celebrating. Fled away. Item1,. ITEM2,. ITEM3, etc... Day, but that's not accurate enough, the tuple name cannot be restored in deserialization to make this public API less useful.

I don't like variable ValueTuple types at the same time. I don't know who designed it, I hope someone can explain it to me, it feels like an oversight. Therefore, only the out variables fit my mind.

Since C # version 1.0, the Try-parse pattern has been present in various value types. The pattern is as follows:

public boolean TryParse(string value, out DateTime date){    // omitted for brevity...}

The function returns a Boolean value that indicates whether the given string value can be parsed. If true, the parsed value is assigned to the data parameter. It is used in the following ways:

if (DateTime.TryParse(someDateString, out var date)){    // date is now the parsed value}else{    // date is DateTime.MinValue, the default value}

This model, though useful, is a bit of a hassle. Sometimes the developer takes the same pattern, regardless of whether the resolution is successful. You can sometimes use the default values. The out variable in C # 7.0 makes this even more complicated, although I don't think it's complicated.

if (DateTime.TryParse(someDateString, out var date)){    // date is now the parsed value}else{    // date is DateTime.MinValue, the default value}

Now we remove the outer declaration of the IF statement block and use the declaration as part of the parameter itself. It is legal to use VAR because the type is known. Finally, the range of the date variable does not change. It is inline back in the declaration before the IF statement block.

You might ask, "Why is this one of my favorite features?" ”...... This does not seem to be a real change.

Don't doubt, it makes our C # code more expressive. Everyone likes the extension method, so consider the following code:

public static class StringExtensions{    private delegate bool TryParseDelegate(string s, out T result);     private static T To(string value, TryParseDelegate parse)        => parse(value, out T result) ? result : default;     public static int ToInt32(this string value)        => To(value, int.TryParse);     public static DateTime ToDateTime(this string value)        => To(value, DateTime.TryParse);     public static IPAddress ToIPAddress(this string value)        => To(value, IPAddress.TryParse);     public static TimeSpan ToTimeSpan(this string value)        => To(value, TimeSpan.TryParse);}

This extension method class looks concise, clear, and powerful. After defining a private delegate that follows the Try-parse pattern, we can write a generic compound method that can pass generic type parameters, strings, and TryParse generic delegates. Now we can safely use these extension methods, using the following:

ublic class Program{    public static void Main(string[] args)    {        var str =            string.Join(                "",                new[] { "James", "Bond", " +7 " }.Select(s => s.ToInt32()));         Console.WriteLine(str); // prints "007"    }}

Editor's note: To learn all the new features of C#7, see the tutorial http://www.dotnetcurry.com/csharp/1286/csharp-7-new-expected-features***

Conclusion

This article is very challenging for me personally. Many features of C # are my favorite, so it's very difficult to pick one of the favorite features in each version.

Each C # version contains powerful and influential features. The C # language team innovates in countless ways-one of which is iterative publishing. At the time of writing, c#7.1 and 7.2 have been officially released. As a C # developer, we are living in an era of exciting language evolution!

Arranging all the features is very indicative to me, which helps to reveal what is actually useful and which affects me the most everyday. I will, as always, strive to become a pragmatic developer! Not every feature is necessary for the job at hand, but it is necessary to understand what is available.

As we look forward to C # 8 's offer and prototype, I am excited about the future of C #, which is confidently and aggressively trying to mitigate the "1 Billion Dollar Error" (note by the Turing Award winner Tony Hoare that a null quote would cost $1 billion).

Reference
    • Https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history
    • Https://en.wikipedia.org/wiki/C_Sharp_ (Programming_language)
    • Https://en.wikipedia.org/wiki/SOLID_ (object-oriented_design)
    • Https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/langversion-compiler-option
    • https://www.wintellect.com/clr-via-c-by-jeffrey-richter/

Handling C # features that have been popular over the years

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.