C#newbie问题在这里。以下代码(取自Christian Gross,Apress的书“C#From Novice to Professional”)给出了错误:
worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));
原因是方法DoAdd()
不接受给定的参数。
public static Func<object> DoAdd(Func<object> cell1, Func<object> cell2) {...}
VS声称上面方法调用中的两个args都是object
类型,而该方法只接受Func<object>
。但两个工作表元素的值的类型为Func<object>
:
worksheet.Add("A2", CellFactories.Static(10.0));
这个Static
方法只返回给定值:
public static Func<object> Static(object value) { return () => value; }
// return type= Func<object>
当我将worksheet["A2"]
转换为Func<object>
时,代码确实有效。
但有些事我不明白。对象实例的类型是Func<object>
。我已经使用GetType()
方法来查看此证明,并将原始元素的对象类型与强制转换对象的对象类型(可接受)进行比较:
Console.Writeline(worksheet["A2"].GetType());
// now cast to the correct type (why can't it do that implicitly, btw?)
Funk1 = worksheet["A2"] as Func<object>;
Console.Writeline(Funk1.GetType());
..他们完全相同! (Type = System.Func'1[System.Object]
)
即使我使用.Equals()
方法比较两种类型,也会返回true
。
然而,VS在方法调用中将第一个对象实例视为类型object
。为什么?为什么被调用的方法“看到”参数与GetType()返回的类型不同?
(如果是的话,GetType()
方法有什么用处?)
非常感谢您的建议/意见! (如果书中的例子出现错误并且您没有看到原因,那么学习该语言有点困难 - 因此,模糊的印象是GetType()
或VS出现问题。
答案 0 :(得分:3)
您需要了解动态类型和静态类型之间的区别。您的worksheet
对象的索引器很可能具有静态类型object
。
public object this[string cell]{get{...}set{...}}
因为类型为object
的C#inherit中的所有对象,所以存储在单元格中的对象引用可以是对任何对象的引用。
也就是说,因为代理人(例如Func<T>
) 是object
,所以可以将其存储在object
引用中:
Func<object> func = ()=>return "foo";
object o = func; // this compiles fine
编译器可以解决这个问题,因为它可以隐式地理解派生类可以存储在对基类的引用中。
编译器无法自动执行的操作是确定对象的动态类型或运行时类型是什么。
Func<object> func = ()=>return "foo";
object o = func; // this compiles fine
func = o; // <-- ERROR
编译器不知道object
中存储的o
实际上是Func<object>
类型。它不应该跟踪这个。这是必须在运行时检查的信息。
func = (Func<object>)o; // ok!
上面的代码行编译成与此类似的行为:
if(o == null)
func = null;
else if(typeof(Func<object>).IsAssignableFrom(func.GetType()))
__copy_reference_address__(func, o); // made up function! demonstration only
else throw new InvalidCastException();
通过这种方式,可以在运行时检查任何演员表(从一种类型转换为另一种类型),以确保它有效且安全。
答案 1 :(得分:2)
其他人给出了准确而详细的答案,但在这里我将尝试用简单的语言解释。
当您编写worksheet["A2"]
时,您确实在调用worksheet
worksheet
有一个名为[]
的成员函数,它接受string
并返回object
成员函数[]
的签名看起来像object this[string id]
因此函数worksheet["A2"]
返回的内容为object
。它可以是int
或string
或许多其他内容。所有编译器都知道它将是object
。
在示例中,您返回Func<object>
。这很好,因为Func<object>
是object
。但是,然后将该函数的结果作为参数传递给另一个函数。
这里的问题是编译器只知道worksheet["A2"]
返回object
。这与编译器一样具体。
因此编译器看到worksheet["A2"]
是一个对象,并且您试图将该对象传递给不接受object
作为参数的函数。
所以在这里你必须通过将返回的对象强制转换为正确的类型来告诉编译器“hey dummy,这是Func<object>
”。
worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));
可以重写为
worksheet.Add("C3", CellFactories.DoAdd((Func<object>)worksheet["A2"], (Func<object>)worksheet["B1"]));
现在编译器知道,即使[]
函数返回object
,它也可以像Func<object>
一样对待它。
旁注:你可能在一行上做得太多了。人们今后可能很难阅读。
为什么被调用的方法'将'参数'看作与GetType()不同的类型返回?
编译器只知道worksheet[]
返回一个对象。编译器无法在编译时调用GetType()
。
GetType()方法有什么用处?
GetType()
方法有很多用途和滥用,但这是一个完全不同的讨论。 ;)
总之,编译器不会假设有关类型的任何内容。这是好东西,因为当您尝试将此方形挂钩装入圆孔时会出现编译时错误。如果编译器没有抱怨,则此错误将在运行时出现,这意味着您可能需要单元测试来检测问题。
你可以通过告诉编译器来解决这个问题“我知道这个事情 是一个圆形挂钩,相信我。”然后它将编译。 如果你撒谎到编译器,那么在执行代码时会出现运行时错误。
这称为“静态输入”。反对的哲学被称为“动态类型”,其中类型检查在运行时完成。静态与动态是一个冗长的争论,如果你感兴趣,你应该自己研究它。
答案 2 :(得分:1)
VS声称上面方法调用中的两个args都是object类型,而该方法只接受Func。但是两个工作表元素的值都是Func
类型
是的,但声明的类型是object
。编译器无法知道实际的运行时类型是Func<object>
,因此需要进行显式转换。