Start with a simple data type
C# 1
public class Product { private string name; public string Name { get { return name; } set { name = value; } } private decimal price; public decimal Price { get { return price; } set { price = value; } } public Product(string name, decimal price) { this.name = name; this.price = price; } public static ArrayList GetSampleProducts() { ArrayList list = new ArrayList(); list.Add(new Product("A", 1)); list.Add(new Product("B", 2)); list.Add(new Product("C", 3)); list.Add(new Product("D", 4)); return list; } public override string ToString() { return string.Format("{0}:{1}", name, price); } }
1. Without generics, ArrayList will create a type incorrectly
2. No private set method
3. It is difficult to create the corresponding attributes by creating private fields first. Similar to Java Bean in Java
C# 2
public class Product { private string name; public string Name { get { return name; } private set { name = value; } } private decimal price; public decimal Price { get { return price; } private set { price = value; } } public Product(string name, decimal price) { Name = name; Price = price; } public static List<Product> GetSampleProducts() { List<Product> list = new List<Product>(); list.Add(new Product("A", 1)); list.Add(new Product("B", 2)); list.Add(new Product("C", 3)); list.Add(new Product("D", 4)); return list; } public override string ToString() { return string.Format("{0}:{1}", name, price); } }
1. Introducing generics to increase code security
2. Separating access rights of get and set, access properties of get and set can be defined separately.
C# 3
public class Product { public string Name { get; private set; } public decimal Price { get; private set; } public Product(string name, decimal price) { Name = name; Price = price; } public Product(){} public static List<Product> GetSampleProducts() { return new List<Product>() { //Call the constructor (string,decimal) to generate a new Product("A", 1), new Product("B", 2), new Product("C", 3), new Product("D", 4), //It also calls the constructor () to generate one. After generation, the values inside are assigned. new Product{ Name="E",Price=5}, }; } public override string ToString() { return string.Format("{0}:{1}", Name, Price); } }
1. Without fields, all are represented by attributes, which enhances consistency.
2. Attributes can be assigned when a constructor is called
C# 4
public class Product { private readonly string name; public string Name { get{ return name; } } private readonly decimal price; public decimal Price { get { return price; } } public Product(string name, decimal price) { this.name = name; this.price = price; } public Product(){} public static List<Product> GetSampleProducts() { return new List<Product>() { //Call the constructor (string,decimal) to generate a new Product("A", 1), new Product("B", 2), new Product("C", 3), new Product("D", 4), }; } public override string ToString() { return string.Format("{0}:{1}", name, price); } }
1. Provides the readonly keyword, which indicates that it can be assigned in the construction method, other places are read-only. Make the code easier to understand.
C# 1 | Weak type set of read-only attributes |
C# 2 | Private Attribute Assignment Method Strong Type Set |
C# 3 | Automated implementation of attributes, enhanced collection and object initialization |
C# 4 | Call constructors and methods more clearly with named arguments |
Sorting and filtering
sort
C# 1
class ProductNameComparer : IComparer { public int Compare(object x, object y) { Product first = (Product)x; Product second = (Product)y; return first.Name.CompareTo(second.Name); } }
1. Mandatory type conversion is unsafe.
2. A class supports only one sort
3. Too much code is written in one sort
C# 2
class ProductNameComparer : IComparer<Product> { public int Compare(Product x, Product y) { return x.Name.CompareTo(y.Name); } }
List<Product> products = Product.GetSampleProducts(); products.Sort(new ProductNameComparer()); foreach (var item in products) { Console.WriteLine(item); }
It's simplified a lot, but it's not concise enough. It's better to compare directly without having to create an additional class.
List<Product> products = Product.GetSampleProducts(); products.Sort( delegate(Product x, Product y) { return x.Name.CompareTo(y.Name); }); foreach (var item in products) { Console.WriteLine(item); }
Instead of creating a new class, define an anonymous method directly.
C# 3
You can also be more concise, even with Lambda expressions
List<Product> products = Product.GetSampleProducts(); products.Sort((x, y) => { return x.Name.CompareTo(y.Name); }); foreach (var item in products) { Console.WriteLine(item); }
Using Extension Method
List<Product> products = Product.GetSampleProducts(); foreach (var item in products.OrderBy(a=>a.Name)) { Console.WriteLine(item); }
The previous methods will change the real ordering of products, while the extended method will not change the original ordering.
C# 1 | Weak type comparison does not support delegation sorting |
C# 2 | Strong type of comparative function delegation comparison anonymity method |
C# 3 | Lambda expression extension method allows lists to remain unsorted |
query
C# 1
List<Product> products = Product.GetSampleProducts(); foreach (var item in products) { if (item.Price > 10m) { Console.WriteLine(item); } }
C# 2
List<Product> products = Product.GetSampleProducts(); List<Product> matches = products.FindAll(delegate(Product p) { return p.Price > 10m; }); Action<Product> print = Console.WriteLine; matches.ForEach(print);
It can be tested more easily, and each operation can be operated independently. Unlike C# 1, all three functions are coupled
A more streamlined version
List<Product> products = Product.GetSampleProducts(); products.FindAll(delegate(Product p) { return p.Price > 10m; }) .ForEach(Console.WriteLine);
C# 3
List<Product> products = Product.GetSampleProducts(); foreach (var item in products.Where(p=>p.Price > 10)) { Console.WriteLine(item); }
C# 1 | Conditions and operations are tightly coupled and both are hard-coded |
C# 2 | Conditions and operations separate anonymous methods to simplify delegation |
C# 3 | Lambda expressions make conditions easier to read |
Processing Unknown Data
C# 2/3
private readonly decimal? price; public decimal? Price { get { return price; } }
C# 4
public Product(string name, decimal? price = null)
Default parameters
C# 1 | Either maintain a logo or change the meaning of the reference type, or use a magic trick |
C# 2/3 | Empty type avoids the tedious use of C# 1. Further simplification of grammatical sugar |
C# 4 | Optional parameters simplify default settings |
LINQ
C# 3
List<Product> products = Product.GetSampleProducts(); var filter = from Product p in products where p.Price > 10 select p; foreach (var item in filter) { Console.WriteLine(item); }
LINQ can query common data types, XML documents, and SQL.
COM and Dynamic Types
C# 4
Simplify COM interoperability
Added dynamic language type dynamic
Easily write asynchronous code
C# 5
async Task await