使用不同的兼容类型覆盖属性

Posted

技术标签:

【中文标题】使用不同的兼容类型覆盖属性【英文标题】:Override Property with different compatible Type 【发布时间】:2011-03-09 15:39:25 【问题描述】:

我需要一个具有属性的基类,我可以在其中派生具有相同属性但不同(兼容)类型的类。基类可以是抽象的。

public class Base

    public virtual object prop  get; set; 


public class StrBase : Base

    public override string prop  get; set;  // compiler error


public class UseIt

    public void use()
    
        List<Base> l = new List<Base>();
        //...
    


我尝试使用泛型,但在使用该类时给我带来了问题,因为我想在列表中存储不同类型的基类。

public class BaseG<T>

    public T prop  get; set; 


public class UseIt

    public void use()
    
        List<BaseG> l = new List<BaseG>(); // requires type argument
        //...
    

【问题讨论】:

【参考方案1】:

这是建议解决方案的另一种方法:

public abstract class Base

    public abstract void Use();
    public abstract object GetProp();


public abstract class GenericBase<T> : Base

    public T Prop  get; set; 

    public override object GetProp()
    
        return Prop;
    


public class StrBase : GenericBase<string>

    public override void Use()
    
        Console.WriteLine("Using string: 0", Prop);
    


public class IntBase : GenericBase<int>

    public override void Use()
    
        Console.WriteLine("Using int: 0", Prop);
    

基本上,我在中间添加了一个通用类,用于存储您正确键入的属性。假设您永远不需要从迭代List&lt;Base&gt; 成员的代码中访问Prop,这将起作用。 (如果需要,您始终可以向 Base 添加一个名为 GetProp 的抽象方法,将泛型转换为对象。)

示例用法:

class Program

    static void Main(string[] args)
    
        List<Base> l = new List<Base>();

        l.Add(new StrBase Prop = "foo");
        l.Add(new IntBase Prop = 42);

        Console.WriteLine("Using each item");
        foreach (var o in l)
        
            o.Use();
        
        Console.WriteLine("Done");
        Console.ReadKey();
    

编辑:添加 GetProp() 方法来说明如何从基类直接访问属性。

【讨论】:

+1:这是对手头问题的干净解决方案。将基类与泛型部分分开。【参考方案2】:

您不能覆盖属性的类型。看看下面的代码:

StrBase s = new StrBase();
Base b = s;

这是完全有效的代码。但是当你尝试这样做时会发生什么?

b.prop = 5;

整数可以转换为object,因为一切都派生自object。但是由于b 实际上是一个StrBase 实例,它必须以某种方式将整数转换为字符串,而它不能。所以这就是为什么你不能覆盖类型。

同样的原则也适用于泛型:

List<BaseG<object>> l = new List<BaseG<object>>();
BaseG<string> s = new BaseG<string>();

// The compiler will not allow this.
l.add(s);

// Here's the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;

这是因为 C# 2.0 中的泛型类型是不变的。 C# 4.0 确实允许这种类型的转换,称为协变和逆变。

解决方案

一个选项是在需要时将object 转换回字符串。您可以在子类中添加类型验证:

public class StrBase : Base

  private string propValue;

  public override object prop 
    get
    
      return this.propValue;
    
    set
    
      if (value is string)
      
        this.propValue = (string)value;
      
    
  

您还可以在子类中公开类型安全的属性:

public class StrBase : Base

  public string strProp 
    get
    
      return (string)this.prop;
    
    set
    
      this.prop = value;
    
  

【讨论】:

第一个解决方案的简单性很有吸引力。但是,如果“prop”被设置为不同的 Type 值会发生什么? 10 年后你会如何看待这个问题?阳光下的任何新鲜事物;) @BernoulliIT,我可能会重新考虑整体设计,因为我真的不需要这个;)根据我的经验,当读取允许不同的数据结构时,往往会出现这样的代码值类型,例如 JSON。通常最好将属性键入为object,并意识到您有时正在处理最好不键入的数据结构。试图把它硬塞进类型系统不会让它变得更好,只会更复杂。 你将如何使用协变/逆变方法解决 OP 的问题?【参考方案3】:

这是可能的,因为 C# 9.0

从 C# 9.0 开始,覆盖方法支持协变返回类型。

(见Microsoft docs)

【讨论】:

【参考方案4】:
public class First

    private int someV;

    public virtual object SomeV  get => someV; set => someV = (int)value; 

    public First()  


public class Two : First

    private string someV;

    public override object SomeV  get => someV; set => someV = value.ToString(); 

    public Two()  

以及这些的使用:

First firstClass = new First();
firstClass.SomeV = 1;

Two twoClass = new Two();
twoClass.SomeV = "abcd";

【讨论】:

以上是关于使用不同的兼容类型覆盖属性的主要内容,如果未能解决你的问题,请参考以下文章

您可以使用不同但“兼容”的签名覆盖接口方法吗?

Python:如何覆盖子类中实例属性的类型提示?

覆盖 FileConfigurationProvider 时使用 System.IO.Stream 检测为不兼容的类型

兼容性测试技巧

移动app测试

Javascript es6覆盖静态属性