C#之路
C#1.0
- 类
- 结构
- 接口
- 事件
- 属性
- 委托
- 表达式
- 语句
- 特性
- 文本
注意:委托与类同级,可以把委托理解成方法声明,当一个方法想用方法作参数时,这时就可以用委托【只是一种应用场景】
委托与事件的区别,事件可以当成一种特殊的委托,主要区别在触发执行上,普通委托声明可以被外部其他类触发执行,而事件只能在自身声明类中执行,两者都可以在外部绑定委托方法,另外事件绑定只能通过+=,委托+,+=都可以
class Program
{
static void Main(string[] args)
{
Student student = new Student();
student.handler1 = Ouput;// 委托
student.handler1 += Ouput;
student.handle2 += Ouput;// 事件
student.handler1();
student.handle2.Invoke(); // 错误写法
student.ExeEvent(); // 正确写法
}
static string Ouput()
{
Console.WriteLine("1");
return "";
}
}
public delegate string DelHandler();
public class Student
{
public DelHandler handler1;
public event DelHandler handle2;
public void ExeEvent()
{
if (handle2 != null)
{
handle2.Invoke();
}
}
}
C#2.0
- 泛型
public class GenericList<T> { public void Add(T input) { } public T Get(int index) { return default(T); } }T就是泛型,泛指一类对象类型,T可以是int,string也可以是你自己定义的类,一般用于统一输入,输出
- 分布类型
- 匿名方法
static void Main(string[] args) { DelHandler student = Ouput;// 正常绑定委托 student += delegate () // 匿名委托 { return ""; }; } static string Ouput() { Console.WriteLine("1"); return ""; }
匿名方法主要用于委托绑定,好处是不需要创建一个独立的方法
- 可以为null的类型
- 迭代器
static void Main() { foreach (int number in SomeNumbers()) { Console.Write(number.ToString() + " "); } // Output: 3 5 8 Console.ReadKey(); } public static System.Collections.IEnumerable SomeNumbers() { yield return 3; yield return 5; yield return 8; }
foreach遍历的对象必须是继承IEnumerable接口的,接口配合关键字yield返回集合值,上面的SomeNumbers是一个方法直接返回IEnumerable类型,如果SomeNumber是一个类,则需要实现IEnumerable的GetEnumerator方法
class Program { static void Main(string[] args) { SomeNumbers someNumbers = new SomeNumbers(); foreach (int number in someNumbers) { Console.Write(number.ToString() + " "); } // Output: 3 5 8 Console.ReadKey(); } } public class SomeNumbers : IEnumerable { public IEnumerator GetEnumerator() { yield return 3; yield return 5; yield return 8; } }
- 协变和逆变
class Program { static void Main(string[] args) { // string【子类】 是继承 object【父类】 string str = "abc"; object obj = str; // 协变【正常变化】 子类->父类 str = (string)obj;// 逆变【非正常变化,容易报错】 父类->子类 str = (string)new object(); // 会报错 } }
对象由子类转父类就是协变【正常变化】
对象由父类转子类就是逆变
C#3.0
- 自动实现的属性
- 匿名类型
var v = new { Amount = 108, Message = "Hello" }; // Rest the mouse pointer over v.Amount and v.Message in the following // statement to verify that their inferred types are int and string. Console.WriteLine(v.Amount + v.Message);
和匿名方法一样,不需要声明一个类就可以初始一个对象出来
- 查询表达式
static void Main() { // Data source. int[] scores = { 90, 71, 82, 93, 75, 82 }; // Query Expression. IEnumerable<int> scoreQuery = //query variable from score in scores //required where score > 80 // optional orderby score descending // optional select score; //must end with select or group // Execute the query to produce the results foreach (int testScore in scoreQuery) { Console.WriteLine(testScore); } } // Outputs: 93 90 82 82
查询表达式是以查询语法表示的查询。 查询表达式是一流的语言构造。 它如同任何其他表达式一样,可以在 C# 表达式有效的任何上下文中使用。 查询表达式由一组用类似于 SQL 或 XQuery 的声明性语法所编写的子句组成。 每个子句进而包含一个或多个 C# 表达式,而这些表达式可能本身是查询表达式或包含查询表达式。
- Lambda表达式
class Program { static void Main(string[] args) { List<Person> persons = new List<Person>(); persons = persons.Where(p => p.Age > 6).ToList(); //所有Age>6的Person的集合 Func<Person, bool> func = p => p.Age > 6; // Lambda表达式 persons = persons.Where(func).ToList(); // 和上面同一个意思 } } public class Person { public string Name { get; set; } public int Age { get; set; } }
Lambda 表达式是一种可用于创建 委托 或 表达式目录树 类型的 匿名函数 。 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数,lambda 表达式
x => x * x指定名为x的参数并返回x的平方值 - 表达式树
class Program { static void Main(string[] args) { Expression<Func<int, int>> expr = x => x + 1; expr.Compile().Invoke(1); // 编译时解析 Expression<Func<int, int, int>> expr2 = (x, y) => { return x + y; }; // 报错 无法将具有语句块【可以理解为不能用大括号】的lambda表达式转换为表达式树 } }
表达式树是在运行时解析表达式树生成相应匿名方法,注意:无法将具有语句块的lambda表达式转换为表达式树
- 扩展方法
- 隐式类型本地变量
- 分部方法
class Program { static void Main(string[] args) { A t = new A(); t.Exe(); } } partial class A { partial void OnSomethingHappened(string s); public void Exe() { OnSomethingHappened("aa"); } } // This part can be in a separate file. partial class A { // Comment out this method and the program // will still compile. partial void OnSomethingHappened(string s) { Console.WriteLine("Something happened: {0}", s); } }
分部方法必须在分部类中,其中一个是签名,另一个是实现,返回值必须是void【注定外面不能直接调取,不知道这个特性有什么意义】
- 对象和集合初始值设定项
List<Cat> cats = new List<Cat> { new Cat(){ Name = "Sylvester", Age=8 }, new Cat(){ Name = "Whiskers", Age=2 }, new Cat(){ Name = "Sasha", Age=14 } };
var numbers = new Dictionary<int, string> { [7] = "seven", [9] = "nine", [13] = "thirteen" };
C#4.0
- 动态绑定
class Program { static void Main(string[] args) { dynamic t = new A(); // 运行时解析 t.Exe(); } } public class A { public void Exe() { Console.WriteLine("aa"); } }
该类型的作用是绕过编译时类型检查。 改为在运行时解析这些操作。
- 命名参数/可选参数
class Program { static void Main(string[] args) { A t = new A(); t.Exe(b: "bb", a: "aa"); // 参数可以不按顺序写 } } public class A { public void Exe(string a, string b, string c = "")// c可选参数 { Console.WriteLine("aa"); } }
- 泛型协变和逆变
class Program { static void Main(string[] args) { // 泛型中 in 代表逆变【参数】 out 代表协变【返回】 in,out只能定义在接口中 // 接口IEnumerable是协变 IComparable是逆变 // 协变 // List<Base> b1 = new List<Derived>(); // 报错 List不支持协变【List中的T不是out】 IEnumerable<Derived> d = new List<Derived>(); // 由子类【List】转父类【IEnumerable】 IEnumerable<Base> b = d; // 再协变一次 由子类【Derived】转父类【Base】 // 逆变 逆变更注重执行时参数传入 List<Circle> circles = new List<Circle> { new Circle(), new Circle(), null, new Circle() }; ShapeAreaComparer comparer = new ShapeAreaComparer(); // 定义父类shape circles.Sort(comparer);// 因为主体是List<Circle>【子类】 所以执行到这参数类型应该是IComparer<Circle> 执行时comparer会由shape会转成circle } } public interface A1<in T>// 正确写法 in 只能在接口或委托 并当参数用 { void Exe(T a); } public interface A2<in T>// 错误写法 { T Exe(int a); } public interface B1<out T>// 正确写法 out 只能在接口或委托 并当返回值用 { T Exe(int a); } public interface B2<out T>// 错误写法 { void Exe(T a); } public class Base { }// 父类 public class Derived : Base { } // 子类 abstract class Shape // 父类 { public virtual double Area { get { return 0; } } } class Circle : Shape// 子类 { public override double Area { get { return Math.PI; } } } class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape> // 逆变接口 { int IComparer<Shape>.Compare(Shape a, Shape b) { if (a == null) return b == null ? 0 : -1; return b == null ? 1 : a.Area.CompareTo(b.Area); } }
委托中Func和Action也是
class Program { public static Derived MyMethod(Base b) { return b as Derived ?? new Derived(); } static void Main(string[] args) { Func<Base, Derived> f1 = MyMethod; // 初始类型 Func<Base, Base> f2 = f1; Base b2 = f2(new Base()); // b2协变 返回类型由子类->父类 Func<Derived, Derived> f3 = f1; Derived d3 = f3(new Derived()); // d3逆变 参数由父类->转子类【在方法中执行】 } } public class Base { } public class Derived : Base { }
协变和逆变在于对象引用地址的变更
- 嵌入的互操作类型
C#5.0
- 异步成员
- 调用方信息特性
static void Main(string[] args) { TraceMessage("msg"); } static void TraceMessage(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) { System.Diagnostics.Trace.WriteLine("message: " + message); System.Diagnostics.Trace.WriteLine("member name: " + memberName); System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath); System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber); }
C#6.0
- 静态导入
- 异常筛选器
- 属性初始值设定
- Expression Bodied成员
- Null传播器
- 字符串内插
- nameof运算符
- 索引初始值设定项
C#7.0
- Out变量
- 元组和析构函数
- 模式匹配
- 本地函数
- 已扩展expression bodied成员
- ref局部变量和返回结果
C#7.1
- async Main方法
- default文本表达式
- 推断元组元素名称
C#7.2
- 引用语义结合值类型
- 非尾随命名参数
- 数字文字中的前导下划线
- private protected访问修饰符
C#7.3
- 无需固定即可访问固定的字段
- 可以重新分配ref本地变量
- 可以使用stackalloc数组上的初始值设定项
- 可以对支持模式的任何类型使用fixed语句
- 可以使用其他泛型约束
- 可以使用元组类型测试==和!=
- 可以在多个位置使用表达式变量
- 可以将属性附加到自动实现的属性的支持字段
- 由in区分的参数的方法解析得到了改进
- 重载解析的多义情况现在变得更少

浙公网安备 33010602011771号