说我有以下类结构:
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);
}
}
这是我希望运行以下代码的结果:
Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B
相反,我得到:
a
b
由于这不起作用,我尝试在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
我还尝试将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
问题是:new
为什么不像其他人一样工作?通用方法知道value
属于B
类型,那么为什么C#在三个案例中将其视为A
?
答案 0 :(得分:6)
泛型方法知道值是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);
}
这是有效的,因为编译器可以针对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);
}
...这不起作用,因为编译器无法解析NameB
的{{1}}。
当您使用虚拟属性时,您有一个声明的成员,其{em>实现在T
中被覆盖...这正是你想要修改行为时通常会做什么。
如果你想根据执行时间类型使用不同的声明成员(事先不知道它),你可以使用B
:
dynamic
答案 1 :(得分:0)
如果您不想使用dynamic关键字,您可以引入两个类型继承的IA接口,并使用IA作为泛型方法的类型约束。这将产生你期望的输出。
public interface IA
{
string Name { get; set; }
}
public class A : IA
{
public string Name { get; set; }
}
public class B : A, IA
{
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 : IA
{
value.Name = name;
Console.WriteLine(value.Name);
}
}