C#泛型:修改派生类中隐藏的继承成员

Bri*_*ntz 3 c# generics

假设我有以下类结构:

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三个中的一个案例呢?

Jon*_*eet 6

泛型方法知道值是B类型

不是在编译时它没有.当编译器看到时value.Name,它必须将其解析为一个成员......并且唯一可用的成员是声明的成员A.

将您的两个属性视为完全独立 - 就好像它们有不同的名称一样.让我们把它们NameANameB.

你的代码是有效的:

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)

......这是行不通的,因为编译器无法解析NameBT.

当您使用虚拟属性时,您有一个声明的成员,其实现被覆盖B...这正是您通常在修改行为时所做的事情.

如果要根据执行时类型使用不同的声明成员(事先不知道它),可以使用dynamic:

public static void SetAndPrintName(dynamic value, string name) {
    value.Name = name;
    Console.WriteLine(value.Name);
}
Run Code Online (Sandbox Code Playgroud)

  • Aaaand我可以停止写我的答案:P (5认同)