如何将C#泛型方法调用分派到专门的方法调用中

时间:2011-03-07 15:48:29

标签: c# generics c#-4.0

我有以下C#类:

public class MyType<T>
{
   public void TryParse(string p_value)
   {
      T value ;

      Parser.TryParse(p_value, out value);

      // Do something with value
   }
}

重点是调用正确的Parser.TryParse方法,具体取决于泛型类型T.

这使用以下静态类:

static public class Parser
{
   static public void TryParse(string p_intput, out object p_output)
   {
      // Do something and return the right value
   }

   static public void TryParse(string p_intput, out double p_output)
   {
      // Do something and return the right value
   }

   static public void TryParse(string p_intput, out int p_output)
   {
      // Do something and return the right value
   }
}

我希望这可行:在最坏的情况下,将调用“对象”TryParse。相反,我有两个编译错误:

  • CS1502:'Parser.TryParse(string,out object)'的最佳重载方法匹配有一些无效的参数
  • CS1503:参数2:无法从'out T'转换为'out object'

问题1:我不明白为什么这不起作用:我可以天真,但不是所有的C#对象都应该来自“对象”吗? 为什么T无法转换为对象?

问题2: 如何将具有泛型类型T的方法分派到正确的非泛型方法(即MyType<T>.TryParse调用右{{1}根据正确的T)类型?

注意

编辑问题以反映原始问题意图(如标题所示:如何将C#泛型方法调用分配到专门的方法调用

4 个答案:

答案 0 :(得分:6)

实际上,refout参数不允许类型变化。因此,要将变量传递给期望out object参数的方法,该变量必须声明为object

来自规范(§10.6.1.2和§10.6.1.3)

  

当形式参数是引用参数时,方法调用中的相应参数必须包含关键字ref,后跟与形式参数相同类型的变量引用(第5.3.3节)。

     

当形式参数是输出参数时,方法调用中的相应参数必须包含关键字out,后跟与形式参数相同类型的变量引用(第5.3.3节)。

请参阅:Why do ref and out parameters not allow type variation?了解原因。

  

奖金问题:如何将通用类型T的方法分派到正确的非通用方法中(即MyType<T>.TryParse根据{{1}的正确类型调用右Parser.TryParse 1}})?

我要把它转回来。你为什么做这个?如果您正在调用T,例如MyType<T>.TryParse,为什么不直接致电MyType<int>.TryParse?什么是这个额外的层购买你?

答案 1 :(得分:2)

我知道这有点低技术,但我遇到了同样的问题,我通过制作一个包含各个解析器的Dictionary<Type, Parser>解决了这个问题。我会对这个问题给出的答案感兴趣。

此致 莫滕

答案 2 :(得分:2)

当前解决方案

我在工作中使用的当前解决方案基于动态调度,即C#4.0上定义的关键字动态。

代码类似于(来自内存):

public class Parser
{
   static public void TryParse<T>(string p_input, out T p_output)
   {
      // Because m_p is dynamic, the function to be called will
      // be resolved at runtime, after T is known...
      m_p.DoTryParse(p_input, out p_output) ;
   }

   // The dynamic keyword means every function called through
   // m_p will be resolved at runtime, at the moment of the call
   private dynamic m_p = new Parser() ;

   // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

   private void DoTryParse(string p_input, out double p_output)
   { /* Do something and return the right value */ }

   private void DoTryParse(string p_input, out int p_output)
   { /* Do something and return the right value */ }

   // etc.

   private void DoTryParse<T>(string p_input, out T p_output)
   {
      // fallback method... There are no dedicated method for T,
      // so p_output becomes the default value for T
      p_output = default(T) ;
   }
}

优雅的部分是它不会失败(如果没有找到更好的签名匹配,将调用回退函数),并且它遵循一个简单的模式(重载函数)。

当然,现实生产代码有些不同,而且更复杂,因为只有一种公共静态方法,我想:

  1. 解析引用对象(类)和值对象(结构)
  2. 解析枚举
  3. 解析可空类型
  4. 我想为用户提供从Parser派生的可能性以及除默认值之外提供自己的重载
  5. 但我想在当前解决方案中使用动态最终与在下面的原始答案中所做的反射相同。只有“符号”会改变。

    结论,我现在有以下方法:

    public class Parser
    {
       static public void TryParse<T>(string p_input, out T p_output)
       {
          // etc.
       }
    }
    

    能够解析任何东西,包括在编译时不知道T的情况下(因为代码是通用的)。

    原始答案

    Jason's answer关于第一个问题(关于编译器错误)是正确的。不过,我没有解决我的问题(根据运行时泛型类型T从泛型方法调度到非泛型方法)。

    我尝试了LukeH's answer,但它不起作用:无论如何都会调用泛型方法(即使删除第二个参数的out限定符 )。

    Morten's answer是最理智的应该有效,但它没有使用反射。

    所以,为了解决我自己的问题,我使用了反射。这需要重写通用的TryParse方法:

    public class MyType<T>
    {
       public void TryParse(string p_value)
       {
          T value = default(T);
    
          // search for the method using reflection
          System.Reflection.MethodInfo methodInfo = typeof(Parser).GetMethod
             (
                "TryParse",
                new System.Type[] { typeof(string), typeof(T).MakeByRefType() }
             );
    
          if (methodInfo != null)
          {
             // the method does exist, so we can now call it
             var parameters = new object[] { p_value, value };
             methodInfo.Invoke(null, parameters);
             value = (T)parameters[1];
          }
          else
          {
             // The method does not exist. Handle that case
          }
       }
    }
    

    如果需要,我可以使用源代码。

答案 3 :(得分:1)

这个问题引起了我的兴趣,所以我做了一些研究,并在Paul Madox找到了一件好事。这似乎可以解决问题。

   public static T SafeParseAndAssign<T>(string val) where T: new()
    {
        try
        {
            T ValOut = new T();

            MethodInfo MI = ValOut.GetType().
              GetMethod("Parse", new Type[] { val.GetType() });

            return (T)MI.Invoke(ValOut, new object[] { val });
        }
        catch
        {
            // swallow exception
        }
        return default(T);
    }