2012-05-31 158 views
88

閱讀this,我知道有可能允許一種方法通過使其成爲通用方法來接受多種類型的參數。在該示例中,以下代碼與類型約束一起使用以確保「U」是IEnumerable<T>通用方法多(OR)類型約束

public T DoSomething<U, T>(U arg) where U : IEnumerable<T> 
{ 
    return arg.First(); 
} 

我發現一些這使得添加多個類型約束,如更多的代碼:

public void test<T>(string a, T arg) where T: ParentClass, ChildClass 
{ 
    //do something 
} 

然而,這種代碼出現強制實施該arg必須既是一個類型的ParentClassChildClass。我想要做的是說,ARG可能是一個類型的ParentClassChildClass以下方式:

public void test<T>(string a, T arg) where T: string OR Exception 
{ 
//do something 
} 

您的幫助表示讚賞一如既往!

+3

在這種方法的主體內,你能做些什麼*以一種通用的方式*(除非多個類型都來自特定的基類,在這種情況下爲什麼不把它聲明爲類型約束)? –

+0

@Damien_The_Unbeliever不確定你在體內是什麼意思?除非你的意思是允許任何類型和手動檢查身體......並在我想寫的實際代碼中(最後一段代碼片段),我希望能夠傳遞一個字符串或異常,所以沒有類關係那裏(除了system.object我想象)。 – Mansfield

+1

另請注意,在'where T:string''中,'string'是**密封的**類沒有用處。你可以做的唯一有用的事情是爲'string'和'T:Exception'定義重載,正如@ Botz3000在他的回答中所解釋的。 –

回答

44

這是不可能的。你可以,但是,過載定義爲特定類型:

public void test(string a, string arg); 
public void test(string a, Exception arg); 

如果這些都是泛型類的一部分,他們將在方法的通用版本是首選。

+1

有趣。這發生在我身上,但我認爲這可能會使代碼更清潔以使用一個函數。啊,非常感謝!出於好奇,你知道這是不是一個特定的原因嗎? (故意將它排除在語言之外嗎?) – Mansfield

+2

@Mansfield我不知道確切的原因,但我認爲你不能以有意義的方式使用泛型參數了。在課堂內部,如果他們被允許是完全不同類型的,他們將不得不像對象一樣對待。這意味着你可以忽略通用參數並提供重載。 – Botz3000

+2

@曼斯菲爾德,這是因爲「或」關係使得事情變得一般而有用。你會*做*反思,弄清楚該做什麼和所有這些。 (YUCK!)。 – Crisfole

5

如果ChildClass表示它是從ParentClass派生的,那麼您可以編寫以下內容來接受ParentClass和ChildClass;另一方面,如果你想使用兩個不同的類型,但它們之間沒有繼承關係,你應該考慮實現相同接口的類型;

public interface ICommonInterface 
{ 
    string SomeCommonProperty { get; set; } 
} 

public class AA : ICommonInterface 
{ 
    public string SomeCommonProperty 
    { 
     get;set; 
    } 
} 

public class BB : ICommonInterface 
{ 
    public string SomeCommonProperty 
    { 
     get; 
     set; 
    } 
} 

然後你可以寫你的通用函數爲;

public void Test<T>(string a, T arg) where T : ICommonInterface 
{ 
    //do something 
} 
+0

好主意,除了我不認爲我能夠做到這一點,因爲我想使用字符串,這是一個密封的類,根據上面的評論... – Mansfield

+0

這實際上是一個設計問題。通用函數用於執行代碼重用的類似操作。如果你打算在方法體中執行不同的操作,分離方法是一種更好的方法(恕我直言)。 – daryal

+0

我在做什麼,實際上是在寫一個簡單的錯誤記錄函數。我希望最後一個參數是一串關於錯誤的信息,或者是一個異常,在這種情況下,我將e.message + e.stacktrace另存爲一個字符串。 – Mansfield

18

Botz答案是100%正確的,這裏有一個簡短的說明:

當你寫一個方法(通用與否),並宣佈該類型的方法需要你定義一個合同的參數:

如果你給我知道怎麼做的那套東西 T型知道怎麼做,我可以提供是「A」的對象: 類型的返回值我聲明,或「 b':某種使用該類型的行爲。

如果你嘗試給它不止一種類型在一個時間(由具有或),或設法得到它返回,可能是不止一種類型的合同引起的模糊值:

如果你給我一個知道如何跳繩或知道如何計算pi到達第15位數字的對象,我會返回一個可以去釣魚的物體,或者可能混合具體的 物體。

問題是,當你進入方法,你不知道他們是否已經給你一個IJumpRopePiFactory。此外,當你繼續使用該方法(假設你已經得到它以神奇編譯),你不確定你是否有FisherAbstractConcreteMixer。基本上它使整個事情變得更加混亂。

的解決問題的方法是二possiblities之一:

  1. 定義定義每個可能的轉變,行爲,或任何一種以上的方法。這是Botz的答案。在編程領域,這被稱爲重載方法。

  2. 定義一個基類或接口,它知道如何完成該方法所需的所有工作,並且有一種方法只需該類型。這可能涉及將stringException包含在一個小類中,以定義如何將它們映射到實現中,但是所有內容都非常清晰易讀。我可以從四年後開始閱讀你的代碼,並且很容易理解發生了什麼。

您選擇哪一個取決於選擇1和選項2的複雜程度以及需要擴展的程度。

因此,對於您的具體情況我會想象你剛剛拉出來自異常的消息或東西:

public interface IHasMessage 
{ 
    string GetMessage(); 
} 

public void test(string a, IHasMessage arg) 
{ 
    //Use message 
} 

現在,所有你需要的是改變一個string方法和Exception到一個IHasMessage。好簡單。

+0

抱歉@ Botz3000,剛纔注意到我拼錯了你的名字。 – Crisfole

+0

或者編譯器可能會將該類型作爲函數內的聯合類型進行威脅,並使返回值與其獲取的類型相同.TypeScript執行此操作。 – Alex

+0

@Alex但這不是C#所做的。 – Crisfole