Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么.NET List Sort()不接受显式声明的委托对象?_C#_Sorting_Generics_Delegates - Fatal编程技术网

C# 为什么.NET List Sort()不接受显式声明的委托对象?

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

快速提问

在上的第二个示例(第二个代码块,具有一个名为CompareDinosByLength的方法)中,排序方法的调用如下:

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);
}