Do not regularly update the translation series, this series of updates no time rule, the style of food translation dishes beg you crossing Master light spray, such as think I translated have a problem please move the original blog address
This blog post is translated from:
Http://www.dotnetcurry.com/csharp/1411/csharp-favorite-features
In this article, please join me in exploring the various versions of C # and share my favorite features in each version. I will demonstrate its merits while emphasizing practicality.
C # My favorite feature-V1 to v7c#1.0 version
The c#1.0 version (ISO-1) is really a very uninteresting thing, nothing particularly exciting, and it lacks a lot of language that developers like. However, there is a special feature that I think is my favorite. -An implicit and explicit interface implementation.
The interface is always in use and is still popular in modern C #. Take the following Idateprovider interface as an example.
public interface IDateProvider{ DateTime GetDate();}
Nothing special, now imagine two implementations-the first of which is implicitly implemented as follows:
public class DefaultDateProvider : IDateProvider{ public DateTime GetDate() { return DateTime.Now; }}
The second display implementation is this:
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.
The above two examples make the implementation more explicit
One of the simplicity of an explicit interface implementation is that it forces the user to rely on the interface. An instance object of a class that explicitly implements an interface does not have an interface member available-instead, the interface itself must be used.
However, when you declare it as an interface or pass the implementation as an argument to the intended interface, the members are available as expected.
This is especially useful when it enforces the use of interfaces. By using interfaces directly, you will not be coupling code to the underlying implementation. Similarly, explicit interfaces implement the ambiguity of handling named or method signatures-and enable a single class to implement multiple interfaces with the same members.
Jeffery Richter in his book CLR via C # warns us about explicit interfaces. The two main concerns are that value types are boxed when they are converted to explicitly implemented interfaces and methods, and derived types cannot call them.
Keep in mind that boxing and unpacking bring additional energy, and as with all programming, you should evaluate test cases to determine which tools are appropriate for the job.
c#2.0 version
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
- Local type
My favorite feature is between generics and iterators , and I've chosen generics, and I'll say why.
It was a very difficult choice for me and I finally decided on generics because I believe I use generics more frequently than write iterators. Many of the solid programming principles are optimized by using generics in C #, as well as helping to keep the code simple. Don't get me wrong, I did write a lot of iterators, which is a feature worth taking in your C #!
Let's look at generics in more detail.
Editor's note: learn how to use generics in C # to improve the maintainability of your application.
Introduction to Generics: the. NET Framework introduces the concept of type parameters, which enables you to design classes and methods 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 that can be used as a packet. It may look like this:
public class DataBag{ public void Add(object data) { // 为了简便起见,我们省略了... } }
At first glance, this seems like a great idea, because you can add anything to an instance of this data object package. But when you really think about what this means, it can be quite worrying.
All added content is implicitly transferred to 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) { // 为了简便起见,我们省略了... }}
Now, for example, the Databag instance allows only the consumer to add a DateTime instance. The world of type safety, no type casting or boxing is fine.
Generic type parameters can also be limited. Generic constraints are powerful and allow for a limited range of available type parameters because they must conform to the appropriate constraints. There are several ways to write generic type parameter constraints, refer to 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, so we can just separate them with commas. The type parameter constraint is immediately enforced, which allows us to be alerted immediately if a compilation error is made. Let's take a look at the constraints of the Databag class below.
public class DataBag where T : class{ public void Add(T value) { // 为了简便起见,我们省略了... }}
Now, if I try to instantiate the databag,c# compiler it will let me know what I did wrong. More specifically, it states that:
Type ' DateTime ' must be a reference type to use as a parameter ' T ' in a generic type or method ' Program.databag '
c#3.0 version
Here is a list of the main features of c#3.0.
- Anonymous type
- Auto-Implement Properties
- Expression tree
- Extension methods
- Lambda expression
- query expressions
I stumble on the edge of the extension method that chooses the lambda expression . But when I think about C # written today, I actually use the lambda operator more than any other C # operator.
I like to write expressive C #.
There are many opportunities in C # to take advantage of lambda expressions and lambda operators. Use the = = lambda operator to separate the left input from the lambda body on the right.
Some developers prefer to consider lambda expressions as a lengthy way to express delegate invocation. The Action,func type is simply a pre-defined generic delegate in the System namespace.
Let's start with a problem we're trying to solve and apply a lambda expression to help us write some expressive and concise C # code.
Suppose we have a large number of records to represent the weather trend information. We might want to do something different about that data, rather than traversing it in a typical loop, because we can deal with the problem in different ways.
public class WeatherData{ public DateTime TimeStampUtc { get; set; } public decimal Temperature { get; set; }}private IEnumerable GetWeatherByZipCode(string zipCode) { /* ... */ }
Since Getweatherbyzipcode's method call returns a IEnumerable, it seems that you might want to iterate over the collection in a loop. Suppose we have a method of calculating the mean temperature, it can do the work.
private static decimal CalculateAverageTemperature( IEnumerable<WeatherData> 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 all the temperatures and their sums in the filtered date range, and then calculate the average. In an iteration is a logical if block that checks whether the weather data is within a specific date range. This can be rewritten as follows:
private static decimal CalculateAverageTempatureLambda( IEnumerable<WeatherData> weather, DateTime startUtc, DateTime endUtc){ return weather.Where(w => w.TimeStampUtc > startUtc && w.TimeStampUtc w.Temperature) .Average();}
As you can see, this is greatly simplified. The logical if block is actually just a predicate, and if the weather date is in range, we will continue to do some extra processing-like filters. Then we add the temperature, so we just have to choose the project. We finally get a filtered list of temperatures, and we can now simply call the average.
Lambda expressions are used as arguments on the where and select extension methods on the universal IEnumerable interface.
c#4.0 version
From the previous release, c#4.0 has a small number of key features.
- Dynamic binding
- Embedded Interop Types
- Generic Covariance and Contravariance
- real name/Optional parameters
All of these features are very useful. But for me, it comes down to real names and optional parameters, not generics and contravariance. Between the two, I discussed which feature I used most often, and for many years it really benefited me the most.
I believe this feature is a real-name/optional parameter . This is a very simple function, but the usability score is very high. I mean, who didn't write a method for overloading or optional parameters?
When you write an optional parameter, you must provide a default value for it. If your parameter is a value type, 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. Let's imagine that we have a repository class and have a GetData method.
public class Repository{ public DataTable GetData( string storedProcedure, DateTime start = default(DateTime), DateTime? end = null, int? rows = 50, int? offSet = null) { //为了简便起见,我们省略了... }}
We can see that the parameter list for this method is quite long, but there are several tasks. Indicates that these values are optional. Therefore, callers can omit them and use the default values. As you might assume, we can call it only by providing a stored procedure name.
var repo = new Repository();var sales = repo.GetData("sp_GetHistoricalSales", rows: 100);
Now that we are familiar with the optional parameter characteristics and how these features work, let's use some real-name parameters here. 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 version
As with the c#4.0 version, there are not many features in the c#5.0 version-but one of these features is very large.
- Async/Wait
- Callerinfoattributes
When C#5.0 was released, it actually changed the way that C # developers wrote asynchronous code. Although there is still a lot of confusion until today, I am 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 bound workloads such as interacting with databases, networks, file systems, and so on. Asynchronous programming helps handle throughput by using non-blocking methods. This method uses a transparent asynchronous state machine in the hang point and the corresponding continuation.
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: Here are some best practices for C # asynchronous programming, using Async Await.
In c#5.0, asynchronous programming is simplified when the language adds two new keyword async and await. These keywords apply to task. The following table will be used as a reference:
A task represents an asynchronous operation. An operation can return a value through a task, or it can return void through a task. When you use the Async keyword to decorate a task return method, it enables the method body to use the await keyword. When you request the return value of the await keyword, the control flow is returned to the caller, and a pause is performed at the point of the method. Resumes execution at the same point when the await operation is complete. Part of the code is as follows!
class IOBoundAsyncExample{ 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. The method is to return a task, which means that our Getjokeasync method will eventually give you a string, or possibly an error.
This method is decorated with the Async keyword, which allows you to use the Wait keyword. We instantiate and use a HttpClient object. Then we call the Getstringasync function, which takes a string URL and returns a task . We wait for the returned task from the Getstringasync call.
When the response is ready, it will continue to occur and control the recovery from where we have been suspended. We then deserialize the JSON into an instance of the result class and return the joke property.
Some of my favorite results.
- Chuck Norris (Chuck Norris) can test the entire application with a single assertion.
- Chuck Norris (Chuck Norris) can compile syntax errors.
- The project manager will never ask Chuck Norris (Chuck Norris) to make an estimate.
The hilarious comes with it! We have learned about c#5.0 's amazing asynchronous programming model.
c#6.0 version
C#6.0 's launch has been a lot of progress, it's hard to choose my favorite features.
- Dictionary initialization
- Exception filter
- Using a lambda expression in an attribute
- nameof-expression
- Null value operator
- Automatic attribute Initialization
- Static Import
- String embedding value
I narrowed the range down to three salient features: The null operator, the string embedding value, and the nameof expression.
Although the nameof expression is great, I use it almost every time to write code, but the other two features are more influential. This makes it very difficult for me to make a decision between the string embedding value and the null operator. I decided that my favorite is the string embedding value, which is why.
The null operator is great, it allows me to write less detailed code, but it does not necessarily prevent errors in my code. However, using string embedding values can prevent run-time errors-This is a triumph in my book.
When you use the $ symbol to start string literals, the string embedding value syntax in C # is enabled. This instructs the C # compiler to insert this string with a variety of C # variables, logic, or expressions. This is a manual string connection or even a string. A major upgrade of the Format method. Consider the following:
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 name attributes for first and last names. We rewrite the ToString method and use String.Format. The problem is that, at compile time, it is easy to make mistakes because the developer obviously wants the last name also as part of the result string, which is evident in the "{0} {1}" parameter. Similarly, developers can easily exchange names or provide two name parameters correctly, but confusing formatting text includes only the first index, and so on ... Now we can consider using string embedding values.
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 embedding values in the overrides of our ToString method. As a developer, it is much more difficult to make these mistakes. Finally, I can also format in an interpolation expression. Note the third embedded value, dateOfBirth is a datetime-so we can use all the standard formats that you are accustomed to. Just use the: operator to separate variables and formatting.
Sample output
· David Pine (Born July 7, 1984)
Edit Note: For more information on the new features of c#6.0, please read the Www.dotnetcurry.com/csharp/1042/csharp-6-new-features
c#7.0 version
From all the features integrated into C # 7.0.
- The expression body of more function members
- Local functions
- Out variable
- Pattern matching
- Local variables and references return
- Tuple and structure
I ended up arguing between pattern matching, tuples and out variables. I finally chose the out variable, which is why.
pattern matching But I don't really use it very often, at least not yet. Maybe I'll use it more in the future, but there's not much I can do with all the C # code I've written so far. Again, it's a great feature and I really see where it's located-it's just not my favorite in c#7.0.
tuples are also a good addition. Tuples are an important part of language, and being a first-class citizen is great. I would say, "Write tem1,. ITEM2,. ITEM3, etc... The days have passed, but this is not necessarily true. Deserialization has lost the name of a tuple, making this public API less valuable
I don't like it either. The fact that the valuetuple type is mutable. I just don't understand the designer's decision. I hope someone can explain it to me, but it feels a bit like negligence. Therefore, I get the attribute of choosing out variable.
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){ // 为了简便起见,我们省略了.....}
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 generated output parameter date. It is used as follows:
DateTime date;if (DateTime.TryParse(someDateString, out date)){ // date现在是解析值}else{ // date是DateTime.MinValue,默认值}
This pattern is useful, but a little cumbersome. Sometimes, regardless of whether the resolution is successful, the developer takes the same course of action. Sometimes it is possible to use the default values. The out variable in c#7.0 makes this more complicated, but it seems less complicated to me.
Examples are as follows:
if (DateTime.TryParse(someDateString, out var date)){ // date现在是解析值}else{ // date是DateTime.MinValue,默认值}
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 leaks from the inline declaration to the top of the If block.
You might ask yourself, "Why is this one of his favorite features?" ..... This feeling really doesn't change much.
But it changed everything!
It makes our C # more expressive. Everyone likes the extension method, yes-please consider the following points:
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 is very concise, strong expression ability. After defining a private delegate that follows the Try-parse pattern, we can write a generic compound function that requires parameters of a generic type, string values to parse, and Tryparsedelegate . Now that we can safely rely on these extension methods, consider the following points:
public class Program{ public static void Main(string[] args) { var str = string.Join( "", new[] { "James", "Bond", " +7 " }.Select(s => s.ToInt32())); Console.WriteLine(str); // 打印 "007" }}
Editor's note: to learn all the new features of c#7, see this tutorial www.dotnetcurry.com/csharp/1286/csharp-7-new-expected-features
Conclusion
This article is very challenging for me personally. I like many features of C #, so it's very difficult to collect only one favorite content per release.
Each newer version of C # contains powerful and impactful features. The C # language team innovates in countless ways-one of which is the introduction point release. At the time of this writing, C # 7.1 and 7.2 were shipped formally. As a C # developer, we live in an exciting language ERA!
However, it is quite insightful for me to classify all of these features, because it helps us understand what is practical and the most affecting my daily development. As always, strive to be a pragmatic developer! Not all of the features available in the language are required for the current task, but it is important to understand what is available.
I am excited about the future of C # when we look forward to C#8 's suggestions and prototypes. It does look promising, and language is actively trying to alleviate the "billions of dollars of mistakes".
Welcome reprint, Reproduced please specify the source of translation (this article), the original source (original blog address), and then thank you to watch
If you feel that my translation is helpful to you, please click on the recommended support:)
The most popular feature in C # calendar years