c#泛型:修改派生类中的隐藏继承成员
本文关键字:隐藏 继承 成员 泛型 修改 派生 | 更新日期: 2023-09-27 18:11:10
假设我有如下的类结构:
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
既然没有工作,我试着在SetAndPrintName
中铸造value
,这是丑陋的,但工作如预期:
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
呢?
泛型方法知道value是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);
}
这是有效的,因为编译器可以根据A
解析NameA
,并且T
被约束为A
。
如果你试图显式地使用NameB
,但是:
// Invalid
public static void SetAndPrintName<T>(T value, string name) where T : A {
value.NameB = name;
Console.WriteLine(value.NameB);
}
…这不起作用,因为编译器无法将NameB
解析为T
。
当你使用虚拟属性时,你就有了一个声明的成员,它的实现在B
中被覆盖…当你想要改变行为时,这正是你通常所做的。
如果你想根据执行时类型使用不同的声明成员(事先不知道),你可以使用dynamic
:
public static void SetAndPrintName(dynamic value, string name) {
value.Name = name;
Console.WriteLine(value.Name);
}
如果你不想使用动态关键字,你可以引入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);
}
}