2017-03-22 48 views
0

我很難理解爲什麼它返回基值而不是實際值。我需要確定有一個Prop,但我想使用實際值而不是基準值。使用基本屬性而不是聲明

public interface IA 
{ 
    string Value { get; set; } 
} 

public class A : IA 
{ 
    public String Value { get; set; } 
} 

public class B 
{ 
    public IA Prop { get; set; } 
} 

public class C : B 
{ 
    public new A Prop { get; set; } 

    public C() 
    { 
     Prop = new A(); 
     Prop.Value = "test C"; 
    } 
} 


public static class D 
{ 
    public static string GetDynamicProp(dynamic item) 
    { 
     return item.Prop.Value; 
    } 

    public static string GetProp<T>(T item) where T : B 
    { 
     return item.Prop.Value; 
    } 

} 

,然後用它:

var test = new C(); 
Console.WriteLine(test.Prop.Value);  // test C 
Console.WriteLine(C.GetDynamicProp(test)); // test C 
Console.WriteLine(D.GetProp(test));  // null reference exception because it is using the `IA Prop` from `B` 

當使用動態類型它的工作,但我不能例如使用智能感知。所以我想確保發送到D.GetProp的對象實現了IA的屬性,但仍使用實際值。

我希望Prop始終是實現IA的類型,但它可以是繼承類型。

+0

>我希望Prop始終是實現IA的類型,但它可以是繼承類型。 你已經知道在編譯時,你在'Prop'中有哪些實際的繼承類型'IA'?或者你只知道運行時? –

+0

@ RonaldRink'd-fens'我在class C中聲明瞭實際的類型。那是當我知道它是什麼。 –

回答

2

根據您的評論你知道實際的類型在編譯時,因此爲了能夠使用智能感知(即具有實際類型的編譯時的知識),並仍然能夠使用所有的派生類,你可以使用一個接口和一個基類的組合與通用Cast<T>方法這樣的通用接口:

using System; 
using System.Text.RegularExpressions; 

public interface IA 
{ 
    string Value { get; set; } 

    T Cast<T>() where T : class, IA; 
} 

public abstract class ABase : IA 
{ 
    public virtual string Value { get; set; } 

    public T Cast<T>() where T : class, IA 
    { 
     return this as T; 
    } 
} 

public class A : ABase 
{ 
    // redundant, not really needed 
    public override String Value { get; set; } 
} 

public class A2 : ABase 
{ 
    // redundant, not really needed 
    public override String Value { get; set; } 

    public String Value2 { get; set; } 
} 

public class B 
{ 
    public IA Prop { get; set; } 
} 

public class C : B 
{ 
    public C() 
    { 
     Prop = new A(); 
     Prop.Value = "test C"; 
    } 
} 

public class C2 : B 
{ 
    public C2() 
    { 
     Prop = new A2(); 
     Prop.Value = "test C2"; 
     Prop.Cast<A2>().Value2 = "test C2 Value2"; 
    } 
} 

public static class D 
{ 
    public static string GetDynamicProp(dynamic item) 
    { 
     Console.WriteLine(item.GetType().FullName); 
     Console.WriteLine(item.Prop.GetType().FullName); 
     return item.Prop.Value; 
    } 

    public static string GetProp<T>(T item) where T : B 
    { 
     Console.WriteLine(item.GetType().FullName); 
     Console.WriteLine(item.Prop.GetType().FullName); 
     return item.Prop.Value; 
    } 

} 

public class Program 
{ 

    public static void Main() 
    { 
     var test = new C(); 

     Console.WriteLine(test.Prop.Value);  // test C 

     Console.WriteLine(D.GetDynamicProp(test)); // test C 

     Console.WriteLine(D.GetProp(test)); 

     var test2 = new C2(); 

     Console.WriteLine(test2.Prop.Value);  // test C2 

     Console.WriteLine(D.GetDynamicProp(test2)); // test C2 

     Console.WriteLine(D.GetProp(test2));  // test C2 

     Console.WriteLine(D.GetProp(test2));  // test C2 

     Console.WriteLine(test2.Prop.Cast<A2>().Value);   // test C2 
     Console.WriteLine(test2.Prop.Cast<A2>().Value2);  // test C2 Value2 

    } 
} 

那麼輸出應該是這樣的:

test C 
C 
A 
test C 
C 
A 
test C 
test C2 
C2 
A2 
test C2 
C2 
A2 
test C2 
C2 
A2 
test C2 
test C2 
test C2 Value2 

雖然是Cast<T>()方法調用需要大約長4倍,比直接投(SomeType) baseType我們發現它在我們的項目中忽略不計(BenchmarkDotNet給我61ns的速度VS在我的開發爲15ns機)。

3

您使用中的new隱藏B.Prop,而不是覆蓋它。

我認爲這是因爲你無法覆蓋它,因爲它與基類中的屬性名稱相同,但是具有不同的類型。你不能這樣做。

解決此問題的一種方法是將屬性的類型更改爲與基類中的相同,並使其變爲虛擬。然後,你可以override它,像這樣:然後

public interface IA 
{ 
    string Value { get; set; } 
} 

public class A : IA 
{ 
    public String Value { get; set; } 
} 

public class B 
{ 
    public virtual IA Prop { get; set; } 
} 

public class C : B 
{ 
    public override IA Prop { get; set; } 

    public C() 
    { 
     Prop = new A(); 
     Prop.Value = "test C"; 
    } 
} 

的代碼將打印「測試C」如您所願。

有一個很好的理由,你不允許用不同類型的屬性重寫 - 這是因爲你可以通過基類來設置屬性,因此如果你被允許的話,你可以違反類型系統。

一個例子會說清楚。假設你有以下幾點:

public interface IMammal 
{ 
} 

public class Dog : IMammal 
{ 
    public void Bark() 
    { 
     Console.WriteLine("Woof"); 
    } 
} 

public class Cat : IMammal 
{ 
    public void Meow() 
    { 
     Console.WriteLine("meow"); 
    } 
} 

public class Pet 
{ 
    public virtual IMammal Animal { get; set; } 
} 

現在進一步假設你被允許覆蓋Pet.AnimalIMammal派生的類型:

public class DoggyPet : Pet 
{ 
    public override Dog Animal { get; set; } // NOT ALLOWED! 
} 

如果被允許,那麼像這樣的代碼將編譯:

DoggyPet doggy = new DoggyPet(); 
doggy.Animal = new Dog(); // OK so far. 
doggy.Bark(); // No problems. 

Pet pet = doggy; // Assign to base class. 
pet.Animal = new Cat(); // Yikes! just changed doggy.Animal to a cat! 
doggy.Bark(); // What happens now! Ooops. 

注:當然,如果你沒有爲屬性的設置那麼這個問題就不會出現,因此從理論上講這個建議立即進行刪除有可能。此功能稱爲「返回類型協方差」 - 但C#不支持。

See this thread for more information about "return type covariance" and a potential workaround.

+0

是的,但考慮到'A'還有其他幾個屬性。在C類中聲明它爲'IA',意味着我只能使用Value來表示intellisense而不是其他屬性。 –

+0

@HugoDelsing是的,的確如此 - 但這就是繼承在C#中的工作原理。沒有簡單的解決方法。 –