2010-10-28 111 views
3

在C#中,可以創建更高階的函數,即。函數g將函數作爲參數。假設我想創建一個給定函數f的函數,並返回擴展其功能的另一個函數。 如何爲返回的增強方法定義參數名稱?的動機是,我正在使用更高階的方法,其中一些產生新的方法..這些可能很難使用,因爲沒有參數名稱等附加到他們。如何在C#中爲高階函數定義參數名稱#

示出了如何gf和分別可在C#中定義的一個例子:

我定義的方法擴展可以EXTENS方法採取T作爲參數並返回一個S

 
static class M 
{ 
    public static Func< T, S> Extend(Func< T, S> functionToWrap) 
    { 
     return (someT) => 
     { 
     ... 
     var result = functionToWrap(someT); 
     ... 
     return result; 
     }; 
    } 
} 

然後我們可以在不改變方法的情況下在我們的類上擴展一個方法。

 
class Calc2 
{ 
    public Func< int, int> Calc; 
    public Calc2() 
    { 
     Calc = M.Extend< int, int>(CalcPriv); 
    } 
    private int CalcPriv(int positiveNumber) 
    { 
     if(positiveNumber < 0) throw new Exception(...); 
     Console.WriteLine("calc " + i); 
     return i++; 
    } 
} 

唉,參數名稱positiveNumber不再可用,因爲唯一可用的信息是Func<int, int> Calc。那是當我通過輸入new Calc2().Calc(-1)來使用擴展方法時,我從IDE中得不到任何幫助,實際上我的論點是錯誤的。

如果我們可以定義一個delegate並將其轉換爲此值將會很好,但這是不可能的。

有什麼建議嗎?

+1

你需要知道參數名稱?您在何處/如何在您發佈的代碼中使用它? – sepp2k 2010-10-28 21:15:03

+0

有沒有這樣做是不同的,只是創建一個繼承M和重寫Extend的類? – 2010-10-28 21:23:19

+0

以上是一個例子,而不是問題。問題是關於能夠使用記錄參數輸入的更高階方法創建方法 – 2010-10-28 21:25:56

回答

2

如果你只是想用命名的參數固定的委託類型,然後你可以定義自己的委託類型:

Func鍵僅僅是這樣定義的:

public delegate TResult Func<in T, out TResult>(T arg) 

所以,你可以定義你想要的參數名稱自己的委託類型。

但在你的例子中,你想保留傳入的委託類型,所以這在這裏不起作用。從理論上講,你可以定義你的函數是這樣的:

public static T Extend(T functionToWrap) 
{ 
} 

不幸的是,制約與正確的簽名(或甚至只是代表的話)的輸入型的代表沒有很好的通用約束。但是如果沒有這些限制,實施會變得如此醜陋,而且你會失去太多的靜態類型安全,以至於IMO不值得。

一種解決方法是使用:

new MyFunc(Extend(f)) 

其中MYFUNC定義你想要的parameternames。

或者你可以做到以下幾點:

public static T ConvertDelegate<T>(Delegate d) 
{ 
    if (!(typeof(T).IsSubclassOf(typeof(Delegate)))) 
     throw new ArgumentException("T is no Delegate"); 
    if (d == null) 
     throw new ArgumentNullException(); 
    MulticastDelegate md = d as MulticastDelegate; 
    Delegate[] invList = null; 
    int invCount = 1; 
    if (md != null) 
     invList = md.GetInvocationList(); 
    if (invList != null) 
     invCount = invList.Length; 
    if (invCount == 1) 
    { 
     return (T)(object)Delegate.CreateDelegate(typeof(T), d.Target, d.Method); 
    } 
    else 
    { 
     for (int i = 0; i < invList.Length; i++) 
     { 
      invList[i] = (Delegate)(object)ConvertDelegate<T>(invList[i]); 
      } 
      return (T)(object)MulticastDelegate.Combine(invList); 
     } 
    } 

public static TDelegate Extend<TDelegate,TArg,TResult>(Func<TArg,TResult> functionToWrap) 
where TDelegate:class 
    {  
     Func<TArg,TResult> wrappedFunc= DoTheWrapping(functionToWrap); 
     return ConvertDelegate<TDelegate>(wrappedFunc); 
    } 

BTW的ConvertDelegate功能,可用於獲取有關代表合作/逆變甚至之前,.NET 4

+0

+1,我沒有意識到你可以像這樣構造一個參數匹配的委託。 – 2010-10-28 22:10:19

+0

我更喜歡調用者構造所需類型的新委託的解決方法。它仍然很短,易於閱讀和類型安全。 – CodesInChaos 2010-10-28 22:18:35

0

這就是匿名代表的美麗;他們是匿名的。 Func是一個接受int並返回int的方法的委託。函數實際做了什麼,因此參數的名稱是無關緊要的。

這將工作的唯一方法是如果Calc是一個命名的委託類型,定義與簽名相同的CalcPriv。它仍然可以按照書面形式工作,包括匿名擴展,但是您將擁有Calc的命名參數。

傳遞信息的另一種方式是使用描述Calc參數的///<summary></summary>標籤的xml-doc Calc。

最後,你可以從Func<T,TResult>派生出來創建一個TakesPositiveInteger<T,TResult>類。這是走的太遠了一點,但如果你想談談自記錄代碼...

+0

將代碼更改爲'public delegate int Calc(int someArg); public Calc CalcM; public Calc2() { CalcM =(Calc)M.Extend (CalcPriv); } '不起作用 – 2010-10-28 21:29:02

1

有可能轉換爲用命名參數委託,通過一個新構造的代表動態結合到下面的函數功能委託方法:

public delegate double CalcFunc(double value); 

static class M 
{ 
    public static Func<T, S> Extend<T,S>(Func<T, S> functionToWrap) 
    { 
     return (someT) => functionToWrap(someT); 
    } 
} 

class Program 
{ 
    private static double Calc(double input) 
    { 
     return 2*input; 
    } 

    [STAThread] 
    static void Main() 
    { 
     Func<double, double> extended = M.Extend<double, double>(Calc); 

     CalcFunc casted = (CalcFunc)Delegate.CreateDelegate(typeof(CalcFunc), extended.Target, extended.Method); 
     Console.WriteLine(casted(2) + " == 4"); 
     Console.WriteLine("I didn't crash!"); 
     Console.ReadKey(); 
    } 
} 

警告一句話:這不會對劇組進行任何編譯時檢查。如果簽名不完全匹配,您將在運行時遇到綁定失敗(除了特別支持.NET 4中的變換)。

+0

哇,太好了,也許Extend方法也應該把委託類型作爲參數,所以它可能是執行Delegate.CreateDelegate(typeof(...))的那個) – 2010-10-28 21:59:26

+0

如果extended是一個多播委託,這是否正確工作?但是由於Extend對多播委託無用,所以在這種情況下check +異常應該足夠了。如果所需的委託類型在創建代碼時已知,那麼您可以簡單地使用新的CalFunc(擴展)。 – CodesInChaos 2010-10-28 22:09:24

+0

@Carlo,請參閱CodeInChaos的解決方案;委託構造函數更乾淨,更安全。 – 2010-10-28 22:11:02