(C#)为什么Visual Studio说它是一个对象,而GetType说它是一个Func ?

C#新手问题在这里。 以下代码(取自Christian Gross,Apress的书“C#From Novice to Professional”)给出了一个错误:

worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"])); 

原因是方法DoAdd()不接受给定的参数。

 public static Func DoAdd(Func cell1, Func cell2) {...} 

VS声称上面的方法调用中的两个args都是object类型,而该方法只接受Func 。 但是两个工作表元素的都是Func类型:

 worksheet.Add("A2", CellFactories.Static(10.0)); 

这个Static方法只返回给定值:

 public static Func Static(object value) { return () => value; } // return type= Func 

当我将worksheet["A2"]Func ,代码确实有效。

但有些事情我不明白。 对象实例的类型是Func 。 我已经使用GetType()方法来查看此certificate,并将原始元素的对象类型与强制转换对象的对象类型(可接受)进行比较:

 Console.Writeline(worksheet["A2"].GetType()); // now cast to the correct type (why can't it do that implicitly, btw?) Funk1 = worksheet["A2"] as Func; Console.Writeline(Funk1.GetType()); 

..他们都完全相同! (Type = System.Func'1[System.Object]

即使我使用.Equals()方法比较两种类型,它.Equals()返回true

然而,VS在方法调用中将第一个对象实例视为类型object 。 为什么? 为什么被调用的方法“看到”参数与GetType()返回的类型不同? (如果是这样, GetType()方法有什么用?)

非常感谢您的建议/意见! (如果书中的例子给出错误并且你没有看到原因,那么学习语言有点难度 – 因此,得到模糊的印象,即GetType()或VS出现问题。)

您需要了解动态类型静态类型之间的区别。 worksheet对象的索引器很可能具有静态类型的object

 public object this[string cell]{get{...}set{...}} 

因为C#中的所有对象都inheritance自类型object ,所以存储在单元格中的对象引用可以是对任何对象的引用。

也就是说,因为委托(例如Func一个object ,它可以存储在一个object引用中:

 Func func = ()=>return "foo"; object o = func; // this compiles fine 

编译器可以全部解决这个问题,因为它隐式地理解派生类可以存储在对基类的引用中。

编译器无法自动执行的操作是确定对象的动态类型或运行时类型

 Func func = ()=>return "foo"; object o = func; // this compiles fine func = o; // <-- ERROR 

编译器不知道存储在o中的object实际上是Func类型。 它不应该跟踪这个。 这是必须在运行时检查的信息。

 func = (Func)o; // ok! 

上面的代码行编译成与此类似的行为:

 if(o == null) func = null; else if(typeof(Func).IsAssignableFrom(func.GetType())) __copy_reference_address__(func, o); // made up function! demonstration only else throw new InvalidCastException(); 

通过这种方式,可以在运行时检查任何演员表(从一种类型转换为另一种类型)以确保其有效且安全。

其他人给出了准确而详细的答案,但在这里我将尝试用简单的语言解释。

当你编写worksheet["A2"]你真的在调用worksheet的成员函数

worksheet有一个名为[]的成员函数,它接受一个string并返回一个object

成员函数[]的签名看起来像object this[string id]

因此函数worksheet["A2"]返回一个object 。 它可以是intstring或许多其他东西。 所有编译器都知道它将是一个object

在示例中,您可以返回Func 。 这很好,因为Func 一个object 。 但是,然后将该函数的结果作为参数传递给另一个函数。

这里的问题是编译器只知道worksheet["A2"]返回一个object 。 这与编译器一样具体。 因此编译器看到worksheet["A2"]是一个对象,并且您试图将该对象传递给不接受object作为参数的函数。

所以在这里你必须通过将返回的对象强制转换为正确的类型来告诉编译器“hey dummy,这是一个Func ”。

 worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"])); 

可以重写为

 worksheet.Add("C3", CellFactories.DoAdd((Func)worksheet["A2"], (Func)worksheet["B1"])); 

现在编译器知道,即使[]函数返回一个object ,它也可以将它视为Func

旁注:你可能在一条线上做得太多了。 人们在将来阅读可能很难。

为什么被调用的方法“看到”参数与GetType()返回的类型不同?

编译器只知道worksheet[]返回一个对象。 编译器无法在编译时调用GetType()

GetType()方法有什么用?

GetType()方法有很多用途和滥用,但这是一个完全不同的讨论。 ;)

总之,编译器不会假设有关类型的任何内容。 这是一件好事,因为当您尝试将此方形钉插入圆孔时,会出现编译时错误。 如果编译器没有抱怨,则此错误将在运行时出现,这意味着您可能需要unit testing来检测问题。

你可以通过告诉编译器来解决这个问题“我知道这个事情一个圆形的钉子,相信我。” 然后它将编译。 如果您对编译器撒谎,则在执行该代码时会出现运行时错误。

这称为“静态类型”。 反对的哲学被称为“动态类型”,其中类型检查在运行时完成。 静态与动态是一个冗长的争论,如果你感兴趣,你应该自己研究它。

VS声称上面的方法调用中的两个args都是object类型,而该方法只接受Func。 但是两个工作表元素的值都是Func类型

是的,但声明的类型是object 。 编译器无法知道实际的运行时类型将是Func ,因此需要进行显式转换。