【问题标题】:Can I use templates dynamically?我可以动态使用模板吗?
【发布时间】:2011-10-14 21:47:58
【问题描述】:

我有一堂课:

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;
        } 

【问题讨论】:

  • 这个编译? parse 方法从何而来?
  • @fantasticfix Sam 想要使用类的 Parse 功能,例如 Int32.Parse 或 Boolean.Parse。

标签: c#


【解决方案1】:

C# 泛型类型 不是 C++ 模板。模板可以让您进行花哨的“搜索和替换”,您可以在其中替换为 T 实现静态解析方法的类型的名称。C# 泛型不是这样的文本搜索和替换机制。相反,它们描述了类型的参数化多态性。使用模板,所需要的只是您替换参数的 特定 参数都是好的。使用通用的所有可能的替换,无论你是否真的这样做,都必须是好的。

更新:

一位评论者问:

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

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

为不熟悉 Haskell 的读者澄清一下:从 C# 2.0 开始,C# 支持“泛型”类型,这是一种比常规类型“更高”的类型。你可以说List&lt;int&gt;,然后为你创建了一个遵循List&lt;T&gt; 模式的新类型,但它是一个专门的整数列表。

Haskell 在其类型系统中支持一种甚至更高的类型。对于泛型类型,您可以说“每个MyCollection&lt;T&gt; 都有一个方法GetValue,它接受一个int 并返回一个T,对于任何你想命名的T”。使用泛型类型,您可以对 T 施加约束并说“此外,T 保证实现 IComparable&lt;T&gt;...” 使用 Haskell 类型类,您可以更进一步说“......而且,T 是保证有一个 static 方法 Parse 接受一个字符串并返回一个 T"。

“Read”类型类特别是声明道德等价的类型类,即“遵循 Read 类型类模式的类 C 是具有接受字符串并返回 C 的 Parse 方法”的类型类。

C# 不支持那种更高的类型。如果是这样,那么我们可以在语言本身中对模式进行类型检查,例如 monad,如今只能通过将它们烘焙到编译器中来进行类型检查(例如,以查询理解的形式。)(参见 @ 987654321@ 了解更多想法。)

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

人们有时会做一些可怕的事情,例如:

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

但在我看来这有点辱骂;它真的不是通用

【讨论】:

  • 当需要等效于 Haskell 的 Read 类型类时,C# 的处理方式是什么?
  • 感谢更新;它很好地解释了为什么在 C# 中无法在编译时检查解决方案。对于在运行时检查的解决方案,您会推荐什么方法;例如反射,静态 Dictionary, ...?
【解决方案2】:

你可以使用反射。您不能通过泛型参数访问静态成员。

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

【讨论】:

  • 与使用反射的任何东西一样慢。 ;)
【解决方案3】:

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

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

但是,您可以使用反射(类似于 typeof(T).GetMethod("Parse") 的方式)来创建 Func&lt;string,T&gt; 委托。

【讨论】:

    【解决方案4】:

    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】:

      您不能在泛型类上调用静态方法。 看这个帖子: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"
      

      【讨论】:

        【解决方案6】:

        虽然您不能完全使用泛型来做到这一点(没有强制特定方法签名的类型约束,只有结构/对象/接口约束)。

        您可以创建一个基类,其构造函数采用 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)
            {}
        }
        

        【讨论】:

          【解决方案7】:

          这是Oleg G's answer 的变体,它消除了对new() 类型约束的需要。这个想法是你为每个想要包含在abs 中的类型创建一个Parser,然后注入它——这也是Func&lt;string, T&gt; 方法的形式化。

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

          【讨论】:

            【解决方案8】:
            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();
                }
              }
            }
            

            【讨论】:

              猜你喜欢
              • 2023-01-16
              • 2023-02-08
              • 2019-08-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-22
              • 2023-03-10
              相关资源
              最近更新 更多