C# 为什么.NET List Sort()不接受显式声明的委托对象?
快速提问 在上的第二个示例(第二个代码块,具有一个名为CompareDinosByLength的方法)中,排序方法的调用如下:C# 为什么.NET List Sort()不接受显式声明的委托对象?,c#,sorting,generics,delegates,C#,Sorting,Generics,Delegates,快速提问 在上的第二个示例(第二个代码块,具有一个名为CompareDinosByLength的方法)中,排序方法的调用如下: dinosaurs.Sort(CompareDinosByLength); 为什么排序方法不需要显式声明的委托,正如我在阅读委托文档时所想的那样?在我找到该示例之前,我尝试这样做: delegate int CompareDinosDel(string first, string second); CompareDinosDel newDel = CompareDin
dinosaurs.Sort(CompareDinosByLength);
为什么排序方法不需要显式声明的委托,正如我在阅读委托文档时所想的那样?在我找到该示例之前,我尝试这样做:
delegate int CompareDinosDel(string first, string second);
CompareDinosDel newDel = CompareDinosByLength;
dinosaurs.Sort(newDel);
但是,我不断收到与委托/委托方法不是适当的比较器有关的错误
这两种方法不都应该起作用吗?考虑以下代码:
public class Foo
{
public int Bar { get; set; }
}
public class SomeOtherFoo
{
public int Bar { get; set; }
}
我是否应该说:
Foo foo = new SomeOtherFoo();
这在C#中也行不通。如果有两种不同的类型具有相同的主体/实现,它们仍然是不同的类型。具有相同属性的两个类仍然是不同的类。具有相同签名的两个不同代表仍然是不同的代表
Sort
方法已经定义了委托类型,您需要匹配它。这很像它定义了一个类,它需要接受这个类作为参数;不能只传入具有相同属性和方法的其他类型
这就是静态类型化语言的含义。另一种类型系统是使用“Duck-Typing”,其中语言不应用变量为特定类型的约束,而是应用具有特定成员集的约束。换句话说,“如果它走路像只鸭子,嘎嘎叫像只鸭子,那就假装它是只鸭子。”这与打字风格相反,“它一定是只鸭子,句号,即使它知道如何走路和嘎嘎叫。”
为什么Sort
方法不需要显式声明的委托
C#允许在需要委托的上下文中使用一个方法组,也就是说,一个没有调用(…)
参数列表而命名的方法。编译器对方法组执行重载解析,就好像方法组是用委托的形式参数类型的参数调用的一样。这决定了应该使用方法组中的哪个方法来创建委托
当方法组被重载解析为委托类型(泛型方法的形式参数类型)时,这种重载解析过程有时会导致涉及方法类型推断的异常情况<代码>排序,幸运的是它不是一种通用的方法,所以这些奇怪的事情不会发生
这一特性被添加到C#2.0中;在此之前,必须通过将方法组转换为委托
new MyDelegate(MyMethod)
我不断收到与委托/委托方法不正确相关的错误Comparer
s。难道这两个都不管用吗
不幸的是,no.C#在委托类型上没有结构标识。即:
delegate void Foo();
delegate void Bar();
...
Foo foo = ()=>{};
Bar bar = foo; // ERROR!
即使Foo
和Bar
在结构上相同,编译器也不允许转换。但是,您可以使用前面的技巧:
Bar bar = foo.Invoke;
这相当于
Bar bar = new Bar(foo.Invoke);
然而,新的条
作为其动作调用foo
;它经历了一个间接层次
这个功能确实有一定的意义
理由一:
您不希望结构标识在其他地方起作用:
struct Point { int x; int y; ... }
struct Pair { int key; int value; ... }
....
Point point = whatever;
Pair pair = point; // ERROR
理由二:
你可能想说:
delegate int PureMethod(int);
并且有一个约定,PureMethod
委托是“纯的”——也就是说,它们所代表的方法不会抛出、总是返回、返回仅从其参数计算的值,并且不会产生任何副作用。这样说应该是错误的
Func<int, int> f = x => { Console.WriteLine(x); return x+1; };
PureMethod p = f;
Func f=x=>{Console.WriteLine(x);返回x+1;};
方法p=f;
因为f
不是纯粹的
然而,事后看来,人们实际上并没有做出充满语义的委托。一个难点是不能将谓词
类型的值分配给Func
类型的变量,反之亦然
如果我们必须从头再来一次,我怀疑代表们在CLR中会有结构性身份
最后,我注意到VB在分配混合委托类型时更加宽容;如果需要,它会自动构建适配器委托。这可能会令人困惑,因为有时它看起来像是保持了引用标识,而实际上并没有,但这符合VB的哲学“只让我的代码工作”
难道这两个都不管用吗
不,因为您将两个非常不同的东西传递到这两个函数调用中
这里的关键是要认识到,在这两种情况下,实际上传递给方法的是委托。在第一种情况下,编译器隐式地为您创建一个正确类型的委托,即使您没有明确要求它这样做。在第二种情况下,您正在创建自己的委托,但类型错误,因此尝试将失败
从.NET2.0开始,C#编译器允许您在许多情况下跳过显式创建委托。如果在需要委托的上下文中使用方法名称,并且编译器可以验证方法签名和委托签名是否匹配,则它将使用该方法隐式构造委托实例。也就是说,不要这样做(“旧”方法)
您现在可以执行以下操作:
this.SubmitButton.Click += this.SubmitButton_Click;
VisualStudio本身仍然会生成较旧的语法,我认为这是因为它仍然可以工作,而且开发人员不值得花时间去处理它,因为它没有什么好处。但是,如果您在自己的代码中使用冗余委托创建,则大多数流行的代码分析工具将标记该冗余委托创建
同样的技术也适用于任何有方法的地方(技术上称为“方法组”,因为一个方法名可以引用多个重载),并将其分配给委托类型的变量。将一个方法作为参数传递给另一个方法是相同类型的赋值操作:您正在“赋值”实际的参数
this.SubmitButton.Click += new System.EventHandler(this.SubmitButton_Click);
this.SubmitButton.Click += this.SubmitButton_Click;
dinosaurs.Sort(CompareDinosByLength);
dinosaurs.Sort(new Comparison<string>(CompareDinosByLength));
dinosaurs.Sort(new CompareDinosDel(CompareDinosByLength));
public class A
{
public int x;
}
public class B
{
public int x;
}
public void Foo(A a) { }
public void Bar()
{
B b = new B();
this.Foo(b);
}