2010-03-17 86 views
6

我目前在2個抽象類中有2個具體方法。一個類包含當前方法,而另一個包含傳統方法。例如。c#委託和抽象類

// Class #1 
public abstract class ClassCurrent<T> : BaseClass<T> where T : BaseNode, new() 
{ 
    public List<T> GetAllRootNodes(int i) 
    { 
     //some code 
    } 
} 

// Class #2 
public abstract class MyClassLegacy<T> : BaseClass<T> where T : BaseNode, new() 
{ 
    public List<T> GetAllLeafNodes(int j) 
    { 
     //some code 
    } 
} 

我想讓相應的方法在應用程序的相對應場景中運行。我打算寫一個委託來處理這個問題。我的想法是,我可以調用委託並編寫邏輯來處理調用哪個方法,具體取決於調用哪個類/項目(至少這是我認爲代表的用途以及它們如何使用)。

不過,我對這個話題的一些問題(經過一些google搜索):

1)是否有可能有知道駐留在不同類別2(或更多)方法的委託? 2)是否有可能創建一個衍生抽象類的代理(如上面的代碼)? (我的猜測是否定的,因爲委託創建傳入類的具體實現) 3)我試圖爲上面的代碼編寫一個委託。但是我在技術上的挑戰:

public delegate List<BaseNode> GetAllNodesDelegate(int k); 
    GetAllNodesDelegate del = new GetAllNodesDelegate(ClassCurrent<BaseNode>.GetAllRootNodes); 

我得到了以下錯誤:

An object reference is required for the non-static field, method, property ClassCurrent<BaseNode>.GetAllRootNodes(int) 

我可能誤會了什麼......但如果我必須在調用的類手動聲明一個代理,並且如上所述手動傳遞函數,那麼我開始質疑委託是否是處理我的問題的好方法。

謝謝。

回答

7

你試圖使用委託(有new構造它們,聲明名爲委託類型)建議你使用C#1.如果你實際使用C#3的方式,它更比這更容易。

首先,您的委託類型:

public delegate List<BaseNode> GetAllNodesDelegate(int k); 

已存在。它只是:

Func<int, List<BaseNode>> 

所以你不需要聲明它自己的版本。其次,你應該把委託想象成一個只有一個方法的接口,你可以「實現」它,而不必寫一個命名類。只需編寫一個lambda,或直接指定一個方法名稱。

Func<int, List<BaseNode>> getNodesFromInt; 

// just assign a compatible method directly 
getNodesFromInt = DoSomethingWithArgAndReturnList; 

// or bind extra arguments to an incompatible method: 
getNodesFromInt = arg => MakeList(arg, "anotherArgument"); 

// or write the whole thing specially: 
getNodesFromInt = arg => 
    { 
     var result = new List<BaseNode>(); 
     result.Add(new BaseNode()); 
     return result; 
    }; 

拉姆達的形式是(arguments) => { body; }。參數用逗號分隔。如果只有一個,可以省略括號。如果不需要參數,請輸入一對空的括號:()。如果身體只有一個陳述長,你可以省略大括號。如果只是一個表達式,則可以省略大括號和return關鍵字。在本體中,實際上可以引用封閉範圍中的任何變量和方法(除了ref/out參數外)。

幾乎沒有任何需要使用new來創建委託實例。很少需要聲明自定義委託類型。使用Func爲返回返回void代表值和Action代表。

每當你需要通過周圍的東西就像一個方法的對象(接口或類是否),然後使用一個委託,而是和你將能夠避免很多亂七八糟的。

特別地,避免定義與一種方法接口。它只是意味着不是能夠寫一個lambda來實現該方法,你必須聲明一個單獨命名的類爲每個不同的實現,與圖案:

class Impl : IOneMethod 
{ 
    // a bunch of fields 

    public Impl(a bunch of parameters) 
    { 
     // assign all the parameters to their fields 
    } 

    public void TheOneMethod() 
    { 
     // make use of the fields 
    } 
} 

一個lambda有效地做一切爲你,從你的代碼中消除這樣的機械模式。你剛纔說:

() => /* same code as in TheOneMethod */ 

它還具有的優點是,可以在封閉的範圍更新變量,因爲你可以直接引用他們(而不是用複製到類的字段的值工作)。如果您不想修改這些值,有時這可能是一個缺點。

+0

如果你希望委託映射到一個特定的方法,如果它是類型'ClassCurrent'('GetAllRootNodes')和另一個方法,如果它是類型'MyClassLegacy'('GetAllLeaveNodes')那麼你將不得不知道類型的情況。而如果它共享一個通用接口,則不需要知道它實現給定接口的類型。用於爲每種類型設置委託的代碼將包含一個if來檢查類型或將特定類型作爲每個允許類型的參數的方法。 – Cornelius 2010-03-17 08:46:52

+0

@Cornelius - 你認爲代表是什麼?這聽起來像你認爲它與C中的函數指針相同。 – 2010-03-17 09:10:35

+2

委託函數比函數指針更強大,因爲它們是「第一類方法」並且具有閉包,但這隻允許您以多態方式傳遞它們,而不會消除上面評論中設置它的複雜性。 – Cornelius 2010-03-17 09:29:11

1

根據某些條件,您可以擁有一個根據不同方法進行初始化的委託。

關於您的問題:
1)我不確定您在「知道」下的含義。您可以將任何方法傳遞給委託,因此如果您可以編寫「知道」其他方法的方法,而不是您可以執行類似委託的方法。
2)同樣,可以使用任何可以執行的方法創建代表。例如,如果您有一個ClassCurrent<T>類型的初始化局部變量,則可以爲ClassCurrent<T>類型的任何實例方法創建委託。
3)委託只能調用實際可以調用的方法。我的意思是你不能撥打ClassCurrent.GetAllRootNodes,因爲GetAllRootNodes不是一個靜態方法,所以你需要一個ClassCurrent的實例來調用它。

代表可以留在任何可以訪問ClassCurrentMyClassLegacy的類中。

例如,您可以創建水木清華這樣的:

class SomeActionAccessor<T> 
{ 
    // Declare delegate and fied of delegate type. 
    public delegate T GetAllNodesDelegate(int i); 

    private GetAllNodesDelegate getAllNodesDlg; 

    // Initilaize delegate field somehow, e.g. in constructor. 
    public SomeActionAccessor(GetAllNodesDelegate getAllNodesDlg) 
    { 
     this.getAllNodesDlg = getAllNodesDlg; 
    } 

    // Implement the method that calls the delegate. 
    public T GetAllNodes(int i) 
    { 
     return this.getAllNodesDlg(i); 
    } 
} 

代表們能包裹靜態和實例方法。唯一的區別是,對於使用實例方法創建委託,您需要擁有該方法的類的實例。

+0

一旦委託例如創建public delegate void del(int i),代碼保留在特定的類中。我在想,代表需要留在具有感興趣方法的課程中,否則將無法找到它們。另外,我不明白爲什麼代表可以創建一個靜態方法? – BeraCim 2010-03-17 07:35:41

+0

我已經更新了答案。 – 2010-03-17 07:47:47

0

你爲什麼要這樣做的代表?這聽起來過於複雜。我只是在一個新的類中創建一個方法,當你需要調用你的方法時,你可以立即創建一個方法。這個類可以給一些上下文信息來幫助它做出決定。然後,我將在新方法中實現邏輯,以決定是調用當前方法還是傳統方法。

事情是這樣的:

public class CurrentOrLegacySelector<T> 
{ 

    public CurrentOrLegacySelector(some type that describe context) 
    { 
    // .. do something with the context. 
    // The context could be a boolean or something more fancy. 
    } 

    public List<T> GetNodes(int argument) 
    { 
    // Return the result of either current or 
    // legacy method based on context information 
    } 
} 

這會給你一個乾淨的包裝器,易於閱讀和理解的方法。

+0

我認爲使用委託的解決方案更加靈活 - 您將需要更少的操作來支持第三種此類方法。另一方面,第三種方法未來可能不會出現:) – 2010-03-17 07:52:27

+0

委託並不複雜,它要簡單得多。 – 2010-03-17 08:19:26

1

讓雙方ClassCurrentMyClassLegacy實現接口INodeFetcher

public interface INodeFetcher<T> { 
    List<T> GetNodes(int k); 
} 

對於ClassCurrent呼叫從接口的實現GetAllRootNodes方法和MyLegacyClassGetAllLeaveNodes方法。

+3

如果您的界面中有一個方法,請改爲使用委託。你將爲自己節省一大筆代碼。一個委託就像一個單一方法的接口,加上大量的語言支持,這意味着每次你想實現它時,你不必編寫單獨的命名類。您要查找的類型是'Func >'。 – 2010-03-17 07:53:49

+0

@Daniel但你將不能夠使用多態委託未聲明的一個共同的基類或接口,你仍然需要建立代表也將需要的代碼相當量。 – Cornelius 2010-03-17 08:03:51

+0

看到我的答案。設置委託需要比聲明和實現接口少得多的代碼。代理也是多態的:調用者只是獲得一個值,他們可以通過調用方法調用,而不是綁定到特定的實現。這完全像一個單一方法的界面,但使用它所需的儀器代碼要少得多。 – 2010-03-17 08:15:05

0

作爲Rune Grimstad建議的主題的一個變體,我認爲你可以使用策略模式(例如
Introduction to the GOF Strategy Pattern in C#
)。

如果您無法更改LegacyClass(因此可能無法輕鬆使用Cornelius建議的「接口方法」),並且您正在使用依賴注入(DI; Dependency injection),這將會特別有趣。 DI會(可能)讓你在正確的位置注入正確的實現(具體策略)。

策略:

public interface INodeFetcher<T> { 
    List<T> GetNodes(int k); 
} 

具體策略:

public class CurrentSelector<T> : INodeFetcher<T> 
{ 
    public List<T> GetNodes(int argument) 
    { 
    // Return the result "current" method 
    } 
} 

public class LegacySelector<T> : INodeFetcher<T> 
{ 
    public List<T> GetNodes(int argument) 
    { 
    // Return the result "legacy" method 
    } 
} 

- >噴射/實例化正確的具體策略。

問候

+0

但接口有一種方法。這只是使用委託消除了大量不必要的手工編碼的祕訣。 – 2010-03-17 08:18:31

+0

我在哪裏錯過了什麼?順便說一下,您可能已經注意到我不擅長委託編程;如果你可以請溫柔:) – scherand 2010-03-17 09:32:07

+0

你看到我的答案?我不確定還有什麼要補充的......如果你只關心今天的需求,那麼你並不需要以任何特定的方式組織你的代碼。幾乎所有的高級語言功能都是關於簡化程序的未來演變。所以今天你可能只有兩個實現,但未來呢? – 2010-03-17 10:54:26