如何为类型获取默认通用委托?

3
我有一个通用的委托,如下所示:

public delegate T SomeHandler<T>(T input);

我有一个通用类,它将委托作为参数传递给构造函数,如下所示:

public class SomeClass<T>
{
  private SomeHandler<T> m_handler;

  public SomeClass(SomeHandler<T> handler)
  {
    m_handler = handler;
  }

  public void DoSomeStuff(T input)
  {
     T result = m_handler(input);
     // some stuff
  }
}

大多数情况下,我会使用默认处理程序来实例化类,除非需要一些特殊情况。因此,我为我使用的类型准备了一些默认处理程序:

public static class DefaultHandlers
{
  public static string DefaultStringHandler(string input)
  {
    return input;
  }
}

在某些情况下,该类型会实例化一个特殊处理程序,该处理程序专门针对其实现进行。

public class Example
{
  private SomeClass<string> m_typicalCase;

  private SomeClass<string> m_specialCase;

  public Example()
  {
    m_typicalCase = new SomeClass<string>(DefaultHandlers.DefaultStringHandler);
    m_specialCase = new SomeClass<string>(SpecialHandler);
  }

  private string SpecialHandler(string input)
  {
    string result;
    // Do something special
    return result;
  }
}

我想为 SomeClass 创建一个默认构造函数,该函数始终使用相同的默认处理程序实例化该类,但由于类型在编译时未知,因此无法返回正确类型的委托。

public class SomeClass<T>
{

  ...

  public SomeClass()
  {
    m_handler = DefaultHandlers.GetDefaultHandler<T>();
  }

  ...

}

像这样

public static class DefaultHandlers
{
  public static SomeHandler<T> GetDefaultHandler<T>()
  {
    if (typeof(T) == typeof(string))
    {
      return DefaultStringHandler;
    }
  }
}

这不起作用是因为DefaultStringHandler返回了一个字符串,而该方法期望返回T类型的值。
我唯一发现的解决办法是创建一个特定类型的子类SomeClass,重载默认构造函数:
public class SomeStringClass : SomeClass<string>
{

  public SomeStringClass()
  : base(DefaultHandlers.DefaultStringHandler)
  {
  }

  public SomeStringClass(SomeHandler<string> handler)
  : base(handler)
  {
  }

}

如果通用类型能够具有类型特定的重载构造函数,并在实例化特定类型的类时使用,那将会很有趣。
public class Foo<T>
{
  public Foo<string>(string input)
  {
  }

  public Foo<int>(int input)
  {
  }

  public Foo(T input)
  {
  }
}

也许有一种更优雅的方式来处理这个问题,使用设计模式,比如策略模式?


那么对于其他类型,您希望默认处理程序执行什么操作?如果它总是只需“返回输入值”,那就很容易。 - Jon Skeet
1
(SomeHandler<T>)DefaultStringHandler 应该可以工作...(正如Jon Skeet所指出的那样,如果它总是标识,你可以使用lambda进行内联,例如:x => x - Alexei Levenkov
我认为我的处理程序示例,它接受输入并返回相同类型的示例不太现实。实际上,有许多处理程序要么将类型T作为输入并输出其他内容,要么使用其他类型作为输入并输出T。有点像类型转换器。我希望默认行为根据T的类型而异。 - Zoltan F
2个回答

1

在Jon Skeet和Alexei Levenkov的评论基础上,从我的理解来看,以下内容可能是你所需要的?

public delegate T SomeHandler<T>(T input);

public class SomeClass<T>
{
    private SomeHandler<T> m_handler;

    public SomeClass()
    {
        m_handler = (T input) => input;
    }

    public SomeClass(SomeHandler<T> handler)
    {
        m_handler = handler;
    }

    public void DoSomeStuff(T input)
    {
        T result = m_handler(input);
        // some stuff
    }
}

另一种方法是将特定于字符串的行为移入单独的类中,如果您想要与特定类型相关联的特定行为,则只需创建该类的实例即可。
public delegate T SomeHandler<T>(T input);

public class SomeClass<T>
{
    protected SomeHandler<T> m_handler;

    protected SomeClass()
    {

    }

    public SomeClass(SomeHandler<T> handler)
    {
        m_handler = handler;
    }

    public void DoSomeStuff(T input)
    {
        T result = m_handler(input);
        // some stuff
    }
}

public class SomeStringClass : SomeClass<string>
{
    public SomeStringClass()
    {
        m_handler = DefaultStringHandler;
    }

    private string DefaultStringHandler(string input)
    {
        // Do default string stuff here...
        return input;
    }

    public SomeStringClass(SomeHandler<string> handler):base(handler)
    {

    }
}

1
你可以使用 dynamic 来获取类似于 SomeClass<string>() 的东西:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Zoltan
{

    public class SomeClass<T>
    {
        private static readonly Func<T,T> FALL_BACK_HANDLER = a => a; //or what have you

        private readonly Func<T,T> m_handler;

        public SomeClass(Func<T,T> handler)
        {
            m_handler = handler;
        }

        public SomeClass()
        {
            m_handler = DefaultHandler.For<T>() ?? FALL_BACK_HANDLER;
        }

        public void DoSomeStuff(T input)
        {
            T result = m_handler(input);
            Console.WriteLine(result);
        }
    }

    public static class DefaultHandler
    {
        public static Func<T,T> For<T>()
        {
            return TypeAware<T>.Default;
        }

        private static class TypeAware<T>
        {
            private static readonly Func<T,T> DEFAULT;
            static TypeAware()
            {
                var type = typeof(T);
                if (type == typeof(string))
                {
                    DEFAULT = a => DefaultHandler.StringHandler((dynamic) a);
                }
                else if (type == typeof(int))
                {
                    DEFAULT = a => DefaultHandler.IntHandler((dynamic) a);
                }
                else
                {
                    DEFAULT = null;
                }
            }

            public static Func<T,T> Default { get { return DEFAULT; } }
        }

        public static string StringHandler(string a)
        {
            return a + " The default handler does some stuff!";
        }

        public static int IntHandler(int a)
        {
            return a + 2;
        }
    }
}

您可以按照以下方式使用SomeClass
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Zoltan
{    
    public class Program
    {
        public static void Main(string[] args)
        {
            var someStringObj = new SomeClass<string>();
            someStringObj.DoSomeStuff("Hello World.");//prints "Hello World. The default handler does some stuff!"

            var someIntObj = new SomeClass<int>();
            someIntObj.DoSomeStuff(1);//prints 3

            var someCustomDoubleObj = new SomeClass<double>(d => d - 2);
            someCustomDoubleObj.DoSomeStuff(3);//prints 1

            Console.Read();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接