方法组的C#扩展方法

时间:2014-04-02 14:14:14

标签: c# extension-methods

我想为方法实现扩展方法。请考虑以下代码示例(http://dotnetfiddle.net/HztiOo):

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        A a = new A();
        // Noticed that Next() is called twice
        Console.WriteLine(a.Next(1));
        Console.WriteLine(a.Next(1));

        // Works
        var withCache = ((Func<int,int>)a.Next).AddCaching();
        withCache = new Func<int,int>(a.Next).AddCaching();
        withCache = ExtensionMethods.AddCaching<int,int>(a.Next);

        // Doesn't work :(
        // withCache = a.Next.AddCaching<int,int>();
        // Func<int,int> withCache = a.Next.AddCaching();

        // Notice that Next() is only called once
        Console.WriteLine(withCache(1));
        Console.WriteLine(withCache(1));
    }
}

public class A
{
    public int Next(int n)
    {
        Console.WriteLine("Called Next("+n+")");
        return n + 1;
    }
}

public static class ExtensionMethods
{
    public static Func<TKey,TVal> AddCaching<TKey,TVal>(this Func<TKey,TVal> fetcher)
    {
        var cache = new Dictionary<TKey, TVal>();
        return k =>
        {
            if (!cache.ContainsKey(k)) cache[k] = fetcher(k);
            return cache[k];
        };
    }
}

我希望能够在没有显式强制转换的情况下调用扩展方法。在上面的两个“不工作”示例中,类型系统应该能够找出自己使用哪个重载...

为什么我不能只使用a.Next.AddCaching<int,int>()

注意:这只是一个示例,我对讨论向方法调用添加缓存的最佳方法不感兴趣,因为这种扩展还有许多其他可能性。

3 个答案:

答案 0 :(得分:5)

根据Eric Lippert blog方法组是无类型表达式。你不能做任何事情,只需处理它。

这就是为什么你不能将它隐式地转换为特定委托并向其添加扩展方法的确切原因

答案 1 :(得分:0)

通过将您的方法作为Func公开,您可以在风格上实现与所需内容相似的功能,如下所示(https://dotnetfiddle.net/BTyJdU)。显然,这涉及到修改类,因此不能仅通过扩展方法来实现。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        A a = new A();
        // Noticed that Next() is called twice
        Console.WriteLine(a.Next(1));
        Console.WriteLine(a.Next(1));

        // Works now :)
        var withCache = a.Next.AddCaching<int,int>();
        withCache = a.Next.AddCaching();

        // Notice that Next() is only called once
        Console.WriteLine(withCache(1));
        Console.WriteLine(withCache(1));
    }
}

public class A
{
    public Func<int,int> Next;

    public A()
    {
        Next = NextInternal;    
    }

    private int NextInternal(int n)
    {
        Console.WriteLine("Called Next("+n+")");
        return n + 1;
    }
}

public static class ExtensionMethods
{
    public static Func<TKey,TVal> AddCaching<TKey,TVal>(this Func<TKey,TVal> fetcher)
    {
        var cache = new Dictionary<TKey, TVal>();
        return k =>
        {
            if (!cache.ContainsKey(k)) cache[k] = fetcher(k);
            return cache[k];
        };
    }
}

我还整理了一个只使用扩展方法的小提琴。它涉及调用对象的扩展而不是方法:https://dotnetfiddle.net/XaLndp

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        A a = new A();
        // Noticed that Next() is called twice
        Console.WriteLine(a.Next(1));
        Console.WriteLine(a.Next(1));

        // An alternative, that uses extension methods only
        var withCache = a.AddCaching<A,int,int>(x => x.Next);

        // Notice that Next() is only called once
        Console.WriteLine(withCache(1));
        Console.WriteLine(withCache(1));
    }
}

public class A
{
    public int Next(int n)
    {
        Console.WriteLine("Called Next("+n+")");
        return n + 1;
    }
}

public static class ExtensionMethods
{   
    public static Func<TKey,TVal> AddCaching<T,TKey,TVal>(this T wrapped, Func<T,Func<TKey,TVal>> fetcher)
    {
        var cache = new Dictionary<TKey, TVal>();
        return k =>
        {
            if (!cache.ContainsKey(k)) cache[k] = fetcher(wrapped)(k);
            return cache[k];
        };      
    }
}

答案 2 :(得分:-1)

您可以为代理编写扩展方法。在您的示例中:

  

为什么我不能使用a.Next.AddCaching()?

在该问题中,a.Next不属于某种类型。扩展方法仅适用于类型。想一想。在AddCaching扩展方法中,您会在this后撰写什么?你需要一个类型。在这种情况下,您使用了委托Func<TKey,TVal>。这意味着它将扩展该代表。对于编译的示例,您需要编写:

((Func<int,int>)a.Next).AddCaching<int,int>()

这将正确编译。此外,由于您在委托中定义泛型类型,您实际上可以这样调用它:

((Func<int,int>)a.Next).AddCaching()

它知道它正在使用代表中的<int,int>

所以,你很接近,你只需要将a.Next转换为一个类型,委托Func<int,int>来编译它。它适用于扩展语言中任何其他类型的规则。