2016-07-20 54 views
1

我有一個抽象類,GenericModel,它定義了一個模板的方法如下:在模板方法的覆蓋訪問中間類屬性

public virtual string DoStuff<TType>(IEnumerable<TType> input) 
    where TType : GenericModel { return null; } 

我然後具有表示特定一類從GenericModel繼承調用BasicModelGenericModel的子集有足夠的共同性來保證另一種結構。雖然這是一個具體的類,它從來沒有實際實例化,而是其他類從它繼承,因爲有一些共同的屬性不一定在每個GenericModel,但在每個BasicModel(例如我們的目的說每BasicModel有一個id財產)。

如果我有一個從BasicModel稱爲ChildModel繼承的類,我想重寫DoStuff(),這是允許的,因爲從BasicModelGenericModel繼承ChildModel繼承。所以我定義的覆蓋方法(在ChildModel)如下:

public override string DoStuff<ChildModel>(IEnumerable<ChildModel> input){ 
    foreach(var data in input){ 
     var foo = data.id; 
     //Do stuff with foo 
    } 
    //Do more stuff 
} 

的問題是,Visual Studio中突出data.id作爲一個錯誤,說data沒有一個名爲id屬性。我的假設是,這是由於模板方法的where TType : GenericModel部分;它會忽略中間的BasicModel,因此我無法訪問BasicModel中定義的屬性。

這似乎很奇怪,我因爲我指出,輸入必須包括ChildModel S的將全部由定義的BasicModel子類,有一個id財產。

有沒有辦法在ChildModel的覆蓋DoStuff()中訪問這些中間類屬性,或者這對我設置繼承的方式是不可能的?是否有任何解決辦法或更改可以讓我們使用這些屬性?

編輯:從評論和答案看,我對模板方法有一個基本的誤解。我真正想要做的是在GenericModel中定義一個通用方法,輸入IEnumerable<GenericModel>,然後在每個子類中被覆蓋,其中覆蓋代替使用IEnumerable<ChildClass>。顯然這個目標可能不適用於我所定義的結構。

+1

在'DoStuff','ChildModel'是一個類型參數,而不是一個具體的類的引用。你的方法有效地試圖用'DoStuff (...)'覆蓋'DoStuff'。你不能用這種方式專門化泛型方法。 – Lee

+0

@Lee不是type參數用我寫的方式來定義輸入參數的類型嗎?我認爲type參數僅僅是爲了讓輸入參數使用許多不同的類型,每個最低級別的類別都有一個類型參數,其中每個實例可以使用這些類別的所有屬性。 –

+1

@ChrisH。不,不是的。你只是給泛型參數一個不同的名字。不管你稱之爲通用參數,名稱絕不會改變它的功能。 – Servy

回答

1

如果你不想CastException(@Fabjan答案),你可以取代這個:

new ChildModel().DoStuff(new List<GenericModel> 
{ 
    new GenericModel(), 
    new BasicModel{id = 2}, 
    new ChildModel{id = 10}, 
    new BasicModel{id = 15} 
}); 

with DoStuff like t他:

var foo = data.id; 
Console.WriteLine(foo); 

你的輸出將是:

2 
10 
15 
+0

因爲它最終成爲我使用的那個,所以我對它進行了標記,但是對於未來的讀者;請參閱Lee的回答,詳細瞭解爲什麼我最初的想法不起作用或者Fabjan回答此背景的答案。 –

1

那麼,目前的邏輯ChildModel類型參數可以只有類型GenericModel,因爲它是不可能重寫泛型類型約束。 因此 - 編譯器沒有辦法知道我們實際上可以通過可轉換爲BasicModel的東西,這就是爲什麼我們無法看到屬性「id」。

要使用它作爲BasicModel我們可以投用LINQ整個集合:

class GenericModel 
{ 
    public virtual string DoStuff<TType>(IEnumerable<TType> input) 
     where TType : GenericModel 
    { 
     return null; 
    } 
} 

class BasicModel : GenericModel 
{ 
    public int id { get; set; } 
} 

class ChildModel : BasicModel 
{ 
    public override string DoStuff<TType>(IEnumerable<TType> input) 
    { 
     var castedInput = input.Cast<BasicModel>(); 

     foreach (var data in castedInput) 
     { 
      var foo = data.id; 
      //Do stuff with foo 
     } 
     //Do more stuff 

     return null; 
    } 
} 
+0

您不能使用非泛型方法覆蓋泛型方法。 – Lee

+0

我對你的意思有點困惑,「它可能是任何東西」(mabye我誤解了類型參數的使用)。我的印象是,在'GenericModel'中我設置了type參數,並且在'ChildModel'的覆蓋中,我創建了一個模板方法的版本,當傳入一個IEnumerable的'ChildModel'時將會使用這個版本。 –

+0

不能使用'where TType:BasicModel',因爲這個模板方法需要可用於從GenericModel繼承的所有內容,而不僅僅是BasicModel。 –

2
public override string DoStuff<ChildModel>(IEnumerable<ChildModel> input) { ... } 

不專業DoStuff只與ChildModel工作,但只是重命名TType參數ChildModel。這相當於

public override string DoStuff<T>(IEnumerable<T> input) { } 

這不會編譯,因爲你還需要從基類保留在TType約束。

DoStuff方法的一般約束意味着覆蓋必須統一處理所有的子類型。這聽起來像你希望每個子類都支持GenericModel的一個特定子類型。在這種情況下,您可以將泛型參數移動到基類,並在每個子類中指定它,例如與此

var castedInput = input.Cast<BasicModel>(); 
foreach (var data in castedInput) 

:以這種方式,如果你有這樣的例子

foreach (var data in input.OfType<BasicModel>()) 

public abstract class BaseClass<TType> where TType : GenericModel { 
    public virtual string DoStuff(IEnumerable<TType> input) { ... } 
} 

public class SubClass : BaseClass<ChildModel> { 
    public override string DoStuff(IEnumerable<ChildModel> input) { ... } 
} 
+0

我對你的建議有一個問題,那就是最終我要這樣調用這個方法:'/ * ChildModel */childModel.DoStuff(/ * IEnumerable */input)',這樣你就可以定義一個單獨的'SubClass'使用Enumerable的'ChildModel';我想要一個'ChildModel'使用'ChildModel'的Enumerable。 –

+0

@ChrisH。 - 你的意思是你想在'GenericModel'上定義'DoStuff',並且每個子類都要指定'TType'作爲它們自己的具體類型?在這種情況下,我建議你定義一個接口並執行'class ChildModel:IDoStuff ',儘管類型系統沒有辦法強制執行類型和提供給IDoStuff 的參數之間的關係。 – Lee

+0

是的,那正是我想要做的;不幸的是,我無法執行這種關係。我會研究這個,謝謝你的建議! –