如何获取类型

时间:2016-01-05 17:23:19

标签: c# generics

我有一个像这样的通用委托:

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)
  {
  }
}

设计模式必须有更优雅的方式,策略可能吗?

2 个答案:

答案 0 :(得分:1)

以Jon Skeet和Alexei Levenkovs的评论为基础。根据我的理解,这样的事情可能就是你所追求的?

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 :(得分: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();
        }
    }
}