假设我有以下类结构:
public class A {
public string Name { get; set; }
}
public class B : A {
public new string Name {
get { return base.Name; }
set { base.Name = value + " set in B"; }
}
}
public static class Test {
public static void SetAndPrintName<T>(T value, string name) where T : A {
value.Name = name;
Console.WriteLine(value.Name);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我希望运行以下代码的结果:
Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B
Run Code Online (Sandbox Code Playgroud)
相反,我得到:
a
b
Run Code Online (Sandbox Code Playgroud)
由于这不起作用,我尝试value进入SetAndPrintName,这是丑陋但按预期工作:
public static void SetAndPrintName<T>(T value, string name) where T : A {
if (value is B) {
((B)(object)value).Name = name;
} else {
value.Name = name;
}
Console.WriteLine(value.Name);
}
----
Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B
Run Code Online (Sandbox Code Playgroud)
我也试过使Name属性虚拟/覆盖,这也有效:
public class A {
public virtual string Name { get; set; }
}
public class B : A {
public override string Name {
get { return base.Name; }
set { base.Name = value + " set in B"; }
}
}
----
Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B
Run Code Online (Sandbox Code Playgroud)
问题是:为什么new不像其他人一样工作?泛型方法知道它value是类型的B,那么为什么C#将它视为A三个中的一个案例呢?
泛型方法知道值是B类型
不是在编译时它没有.当编译器看到时value.Name,它必须将其解析为一个成员......并且唯一可用的成员是声明的成员A.
将您的两个属性视为完全独立 - 就好像它们有不同的名称一样.让我们把它们NameA和NameB.
你的代码是有效的:
public static void SetAndPrintName<T>(T value, string name) where T : A {
value.NameA = name;
Console.WriteLine(value.NameA);
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为编译器可以解析NameA反对A,并且T被约束的A.
但是,如果您尝试明确使用NameB:
// Invalid
public static void SetAndPrintName<T>(T value, string name) where T : A {
value.NameB = name;
Console.WriteLine(value.NameB);
}
Run Code Online (Sandbox Code Playgroud)
......这是行不通的,因为编译器无法解析NameB的T.
当您使用虚拟属性时,您有一个声明的成员,其实现被覆盖B...这正是您通常在修改行为时所做的事情.
如果要根据执行时类型使用不同的声明成员(事先不知道它),可以使用dynamic:
public static void SetAndPrintName(dynamic value, string name) {
value.Name = name;
Console.WriteLine(value.Name);
}
Run Code Online (Sandbox Code Playgroud)