2010-06-29 75 views
14

我需要一個基類與屬性,我可以派生具有相同屬性但不同(兼容)類型的類。基類可以是抽象的。覆蓋屬性與不同的兼容類型

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 
     //... 
    } 
} 

回答

22

下面是一個替代方法提出的解決方案:

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<Base>成員的代碼中的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()方法來說明如何從基類直接訪問屬性。

+0

+1:這是一個乾淨的解決手頭問題的方法。從通用部分中分離出基類。 – 2010-06-29 15:16:43

6

您不能覆蓋屬性的類型。看看下面的代碼:

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