我可以动态使用模板吗?

时间:2011-10-14 21:47:59

标签: c#

我有一个班级:

class abc <T> {
  private T foo; 
  public string a {
    set {
       foo = T.parse(value);
    }

    get{
       return foo.toString();
    }

  }
}

然而,T.parse命令给了我一个错误。任何人都可以做我想做的事情吗?

我将它用作其他一些派生类的基类。

编辑:

我结束了我们的工作:

Delegate parse = Delegate.CreateDelegate(typeof(Func<String, T>), typeof(T).GetMethod("Parse", new[] { typeof(string) }));

我在构造函数

中执行过一次

然后我在我的财产中执行以下操作:

        lock (lockVariable)
        {
            m_result = (T)parse.DynamicInvoke(value);
            dirty = true;
        } 

8 个答案:

答案 0 :(得分:8)

C#泛型类型不是C ++ 模板。模板允许您进行花哨的“搜索和替换”,您将替换为T实现静态解析方法的类型的名称.C#泛型不是像这样的文本搜索和替换机制。相反,他们在类型上描述参数化多态。使用模板,所需的只是您替换参数的特定参数都是好的。使用通用的每个可能的替换无论你是否真的这样做,都必须做得好。

UPDATE:

评论者问道:

  

当需要等同于Haskell的Read类型时,C#的处理方式是什么?

现在我们来讨论原始问题背后的深层问题。

为不熟悉Haskell的读者澄清:自C#2.0以来,C#支持“通用”类型,这种类型比常规类型“更高”。你可以说List<int>并为List<T>模式后面的一个新类型,但它是一个特定的整数列表。

Haskell在其类型系统中支持甚至更高类型。对于泛型类型,您可以说“每个MyCollection<T>都有一个方法GetValue,它接受​​一个int并返回一个T,对于您想要命名的任何T”。对于泛型类型,你可以在T上设置约束并说“而且,T保证实现IComparable<T> ...”使用Haskell类型类,你可以更进一步说道德等同于“...而且, T保证有一个静态方法Parse,它接受​​一个字符串并返回一个T“。

“Read”类型类特别是声明道德等价物的类型类“遵循读类型类模式的类C是一个采用字符串Parse并返回C的方法”。

C#不支持那种更高级的类型。如果确实如此,那么我们可以在语言本身中检测模式,例如monad,现在只能通过将它们编入编译器来进行类型检查(例如,以查询理解的形式)。(参见{ {3}}以获得更多想法。)

由于无法在类型系统中表示该想法,因此您很难停止使用泛型来解决此问题。类型系统根本不支持这种通用性级别。

人们有时做一些可怕的事情:

static T Read<T>(string s)
{
    if (typeof(T) == typeof(int)) return (T)(object)int.Parse(s);
    if ...

但在我看来,这有点滥用;它确实不是泛型

答案 1 :(得分:3)

你可以使用反射。您无法通过通用参数访问静态成员。

class Abc<T> {
    private T foo; 
    public string a {
        set {
            foo = Parse<T>(value);
        }

        get {
            return foo.ToString();
        }
    }

    static T Parse<T>(string s)
    {
        var type = typeof(T);
        var method = type.GetMethod("Parse", new[] { typeof(string) });
        return (T)method.Invoke(null, new[] { s });
    }
}

答案 2 :(得分:2)

C#没有模板。 .NET泛型不像C ++模板那样工作。

使用适当的约束,您可以对具有泛型类型的参数使用实例方法,但是无法约束静态成员。

但是,您可以使用typeof(T).GetMethod("Parse")之类的反射来制作Func<string,T>代表。

答案 3 :(得分:1)

您不能在泛型类上调用静态方法。 看看这篇文章:Calling a static method on a generic type parameter

但这是一个小解决方法:

public interface iExample
{
    iExample Parse(string value);
}

class abc<T> where T : iExample, new()
{
    private T foo;
    public string a
    {
        set
        {
            foo = (T)(new T().Parse(value));
        }

        get
        {
            return foo.ToString();
        }

    }
}

因此,如果您有一个实现iExample

的类
public class SelfParser : iExample
{
    public iExample Parse(string value)
    {
        return new SelfParser();
    }
}

您将能够像这样使用它:

abc<SelfParser> abcInstance = new abc<SelfParser>();
abcInstance.a = "useless text";
string unParsed = abcInstance.a; // Will return "SelfParser"

答案 4 :(得分:1)

通用参数中不知道T.parse。你必须让它知道。 不要用反射。在这种情况下,它的速度很慢,而且通常是一个糟 以正确的方式使用泛型。

您必须指定T只能是实现包含解析方法的接口的类:

        class abs<T> where T : IParsable<T>
        {
        //your implementation here
        }

        interface IParsable<T>
        {
           T Parse(string value);
        }

        public class Specific : IParsable<Specific>
        {
            public Specific Parse(string value)
            {
                throw new NotImplementedException();
            }
        }

答案 5 :(得分:0)

虽然你不能用Generics做到这一点(没有类型约束来强制执行特定的方法签名,只有struct / object / interface约束。)

您可以创建一个基类,其构造函数采用Parse方法。请参阅底部的Int32实现。

class MyParseBase <T>
{
  public MyBase (Func<string,T> parseMethod)
  {
     if (parseMethod == null)
        throw new ArgumentNullException("parseMethod");
     m_parseMethod = parseMethod;
  }
  private T foo; 
  public string a {
    set
    {
       foo = m_parseMethod(value);
    }
    get
    {
       return foo.toString();
    }
  }
 }

class IntParse : MyParseBase<Int32>
{
    public IntParse()
       : base (Int32.Parse)
    {}
}

答案 6 :(得分:0)

这是Oleg G's answer的变体,无需new()类型约束。我们的想法是为Parser中包含的每种类型设置一个abs,并将其注入 - 这也是Func<string, T>方法的形式化。

interface IParser<T>
{
    T Parse(string value);
}    

class abs<T> 
{
    private readonly IParser<T> _parser;
    private T foo; 

    public abs(IParser<T> parser) 
    { 
      _parser = parser; 
    }

    public string a {
    set 
    {
      foo = _parser.Parse(value);
    }
    get
    {
      return foo.ToString();
    }     
}

答案 7 :(得分:0)

class abc<T> {
  private T foo; 
  public string a {
    set {
      var x_type = typeof(T);
      foo = (T)x_type.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, value, new []{value});
    }

    get{
       return foo.ToString();
    }
  }
}