繁体   English   中英

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

[英]C# Generics: Modify hidden inherited member in derived class

假设我有以下类结构:

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属性设置为virtual / overridden,这也是有效的:

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工作不像其他人一样? 泛型方法知道valueB类型,那么为什么C#在三个案例中将其视为A

泛型方法知道值是B类型

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

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

你的代码是有效的:

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);
}

...这不起作用,因为编译器无法为T解析NameB

当你使用虚拟属性时,你有一个声明的成员,它的实现B被覆盖...这正是你想要修改行为时通常所做的。

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

public static void SetAndPrintName(dynamic value, string name) {
    value.Name = name;
    Console.WriteLine(value.Name);
}

如果您不想使用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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM