C#规范整理·泛型委托事件
2021-05-30 14:16
标签:模式 compare 不可 sof 出错 col interface 生成 play 泛型的优点是多方面的,无论是泛型类还是泛型方法都同时具备可重用性、类型安全和高效率等特性,这都是非泛型类和非泛型方法无法具备的 在编码过程中,应该始终考虑为泛型参数设定约束。约束使泛型参数成为一个实实在在的“对象”,让它具有了我们想要的行为和属性,而不仅仅是一个ob-ject。 指定约束示例: 指定参数具有无参数的公共构造方法。 where T:new() 注意,CLR目前只支持无参构造方法约束。 可以对同一类型的参数应用多个约束,并且约束自身可以是泛型类型。 有些算法,比如泛型集合List<T>的Find算法,所查找的对象有可能会是值类型,也有可能是引用类型。在这种算法内部,我们常常会为这些值类型变量或引用类型变量指定默认值。于是,问题来了:值类型变量的默认初始值是0值,而引用类型变量的默认初始值是null值,显然,这会导致下面的代码编译出错: 代码"T t=null;"在Visual Studio编译器中会警示:错误1不能将Null转换为类型形参“T”,因为它可能是不可以为null值的类型。请考虑改用“default(T)”. 在FCL中每一类委托声明都代表一类特殊的用途,虽然可以使用自己的委托声明来代替,但是这样做不仅没有必要,而且会让代码失去简洁性和标准性。在我们实现自己的委托声明前,应该首先查看MSDN,确信有必要之后才这样做。 在实际的编码工作中熟练运用它,避免写出烦琐且不美观的代码。 如果匿名方法(Lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到该闭包对象中,即将for循环中的变量i 修改成了引用闭包对象(编译器自动创建)的公共变量i。 以上结果全部输出5; 这段代码所演示的就是闭包对象。所谓闭包对象,指的是上面这种情形中的TempClass对象(在第一段代码中,也就是编译器为我们生成的“<>c__DisplayClass2”对象)。如果匿名方法(Lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到该闭包对象中,即将for循环中的变量i修改成了引用闭包对象的公共变量i。这样一来,即使代码执行后离开了原局部变量i的作用域(如for循环),包含该闭包对象的作用域也还存在。理解了这一点,就能理解代码的输出了。 理解C#中的委托需要把握两个要点: 首先没有event加持的委托,我们可以对它随时进行修改赋值,以至于一个方法改动了另一个方法的委托链应用,比如赋值为null,另外一个方法中调用的时候将抛出异常。 以上代码编译会出现错误警告: 有了上面的event加持,但是还不能够规范。 微软为事件模型设定的几个规范: 报错: 无法从“ConsoleApplication4.ISalary 实际上,只要泛型类型参数在一个接口声明中不被用来作为方法的输入参数,我们都可姑且把它看成是“返回值”类型的。所以,泛型类型参数这种模式是满足“协变”的定义的。但是,只要将T作为输入参数,便不满足“协变”的定义了。如: 编译会提示:差异无效:类型参数“T”必须是在“ISalary 除了11中提到的使用泛型参数兼容泛型接口的不可变性外,还有一种办法就是为接口中的泛型声明加上out关键字来支持协变。 FCL 4.0对多个接口进行了修改以支持协变,如IEnumerable<out T>、IEnumerator<out T>、IQuerable<out T>等。由于IEnumerable<out T>现在支持协变,所以上段代码在FCL 4.0中能运行得很好。 委托中的泛型变量天然是部分支持协变的。 因为存在下面这样一种情况,所以编译通不过: 要让上面的代码编译通过,同样需要为委托中的泛型参数指定out关键字: FCL 4.0中的一些委托声明已经用out关键字来让委托支持协变了,如我们常常会使用到的: 逆变是指方法的参数可以是委托或泛型接口的参数类型的基类。FCL 4.0中支持逆变的常用委托有: 举例: 在上面的这个例子中,如果不为接口IMy-Comparable的泛型参数T指定in关键字,将会导致Test(p, m)编译错误。由于引入了接口的逆变性,这让方法Test支持了更多的应用场景。在FCL4.0之后版本的实际编码中应该始终注意这一点。 如有需要, 上一篇的《C#规范整理·集合和Linq》也可以看看! 深入理解协变和逆变传送门《逆变与协变详解》 C#规范整理·泛型委托事件 标签:模式 compare 不可 sof 出错 col interface 生成 play 原文地址:https://www.cnblogs.com/zhan520g/p/11026778.html1.总是优先考虑泛型
2.避免在泛型类型中声明静态成员
非泛型类型中的泛型方法并不会在运行时的本地代码中生成不同的类型。
例如:static void Main(string[]args)
{
Console.WriteLine(MyList.Func
3.为泛型参数设定约束
4.使用default为泛型类型变量指定初始值
public T Func
代码"T t=0;"会警示:错误1无法将类型“int”隐式转换为“T”。
改进public T Func
5.使用FCL中的委托声明
//如用于表示注册事件方法的委托声明:
public delegate void EventHandler(object sender,EventArgs e);
public delegate void EventHandler
6.使用Lambda表达式代替方法和匿名方法
7.小心闭包中的陷阱
示例如下:static void Main(string[]args)
{
Listlists=new List();
for(int i=0;i
{
Console.WriteLine(i.ToString());
};
lists.Add(t);
}
foreach(Action t in lists)
{
t();
}
}
另外一种实现方式;static void Main(string[]args)
{
List<Action>lists=new List<Action>();
TempClass tempClass=new TempClass();
for(tempClass.i=0;tempClass.i<5;tempClass.i++)
{
Action t=tempClass.TempFuc;
lists.Add(t);
}
foreach(Action t in lists)
{
t();
}
}
class TempClass
{
public int i;
public void TempFuc()
{
Console.WriteLine(i.ToString());
}
}
8.了解委托的本质
9.使用event关键字为委托施加保护
如果有event加持的时候,我们修改的时候,比如fl.FileUploaded=null;
fl.FileUploaded=Progress;
fl.FileUploaded(10);
事件 “ConsoleApplication1.FileUploader.FileUploaded ”
只能出现在+=或-=的左边(从类型“ConsoleApplication1.FileUploader”中使用时除外)10.实现标准的事件模型
EventHandler的原型声明:public delegate void EventHandler(object sender,EventArgs e);
11.使用泛型参数兼容泛型接口的不可变性
例如:class Program{
static void Main(string[]args)
{
ISalary
要让PrintSalary完成需求,我们可以使用泛型类型参数:static void PrintSalary
interface ISalary
12.让接口中的泛型参数支持协变
out关键字是FCL 4.0中新增的功能,它可以在泛型接口和委托中使用,用来让类型参数支持协变性。通过协变,可以使用比声明的参数派生类型更大的参数。通过下面例子我们应该能理解这种应用。
比如:static void Main(string[]args)
{
ISalary
在我们自己的代码中,如果要编写泛型接口,除非确定该接口中的泛型参数不涉及变体,否则都建议加上out关键字。协变增大了接口的使用范围,而且几乎不会带来什么副作用。13.理解委托中的协变
比如:public delegate T GetEmployeeHanlder
GetEmployeeHanlder
public delegate T GetEmployeeHanlder
public delegate TResult Func
14.为泛型类型参数指定逆变
Func
class Program
{
static void Main()
{
Programmer p=new Programmer{Name="Mike"};
Manager m=new Manager{Name="Steve"};
Test(p,m);
}
static void Test
总结
文章标题:C#规范整理·泛型委托事件
文章链接:http://soscw.com/index.php/essay/89567.html