【问题标题】:Overload generic method from interface从接口重载泛型方法
【发布时间】:2016-07-26 08:33:31
【问题描述】:

我有一个通用方法的接口:

interface IConverter
{
    void Convert<T>(T value);
}

我想为任何类型实现它,但我也知道string 的方法逻辑可以简化,所以我想专门为string 重载它(如this question):

class Converter : IConverter
{
    public void Convert<T>(T value)
    {
        Console.WriteLine("Generic Method " + value);
    }

    public void Convert(string value)
    {
        Console.WriteLine("String Method " + value);
    }
}

当我有Converter 的实例并直接调用方法时,这很好用。代码

var converter = new Converter();
converter.Convert("ABC");
converter.Convert(123);

输出

String Method ABC
Generic Method 123

但是,当我使用接口时(就像在任何带有 DI 的应用程序中一样),我无法调用 string 的重载。代码

var converter = (IConverter)new Converter();
converter.Convert("ABC");
converter.Convert(123);

输出:

Generic Method ABC
Generic Method 123

有没有办法实现string重载方法的调用无需类型检查之类的

if (typeof(T) == typeof(string))
    ...

?

【问题讨论】:

    标签: c#


    【解决方案1】:

    没有。您将converter 转换为IConverter,因此您的converter 变量上只有一种方法可见:Convert&lt;T&gt;(T value)

    您可以通过将Convert(string) 添加到接口来​​克服这个问题,或者根本不转换到接口并继续使用您的Converter 类。只有这样,其他方法才会可见。

    【讨论】:

    • 好的。但据我了解,JIT 将创建方法的字符串版本。我已经实现了这样的字符串版本......
    • 它仍然使用从接口已知的方法。它不会因为看起来一样就神奇地调用另一个方法。
    • @KyryloM:JITter 将采用通用的 Convert&lt;T&gt; 方法并创建一个 JITtted 的专用 Convert&lt;string&gt; 方法,就像它应该做的那样。就 C# 而言,您的另一种方法与使用完全不同名称的方法相同。
    • 也许你是对的。我已经从堆栈跟踪中检查了完整的方法名称。对于通用版本,它是 Converter.Convert[T](T),对于字符串方法 -​​ Converter.Convert(System.String)
    【解决方案2】:

    有两种方式,

    要么在界面中包含该方法,如

    interface IConverter
    {
        void Convert<T>(T value);
        void Convert(string value);
    }
    

    将特定于string 的方法创建为扩展方法

    public static void Convert(this string str, string value)
    {
      // code here
    }
    

    【讨论】:

    • 我猜你的意思是public static void Convert(this IConverter str, ...
    • @Jcl,不,我的意思是我说的。这是string类型的扩展方法
    • 那就不一样了...你需要这样做:stringVariable.Convert(anotherString);... OP 想要:iconverterVariable.Convert(someString)
    • @Jcl,我没有提到它是一种替代方法,同时将方法本身包含在接口定义中
    • 再次,这是一个对问题没有意义的替代方案......最多:public static void Convert(this string str) 没有string value 参数。 OP 有 一个 字符串变量,您的扩展需要 两个 (您调用方法的一个,作为参数传入的一个)
    【解决方案3】:

    也许你的设计是倒置的,你的界面应该是通用的:

    interface IConverter<T>
    {
        void Convert(T value);
    }
    

    这将允许您以特定方式实现特定类型:

    class BaseConverter<T> : IConverter<T>
    {
        public void Convert(T value) 
        { /* some common way */ }
    }
    
    class StringConverter : IConverter<string>
    {
        public void Convert(string value) 
        { /* strings get converted here */ }
    }
    

    您可能会有一个“工厂”类来提供正确的转换器实例:

    class ConverterFactory
    {
        // this is basically "poor man's" service locator,
        // but the same idea works with DI, where you
        // would register individual comparers 
    
        public IConverter<T> Create<T>()
        {
             // use a Dictionary, or add that
             // typeof(T) == typeof(string) check here
        }
    }
    

    这类似于,例如,IComparer&lt;T&gt; 在 .NET BCL 中的定义,加上 Comparer&lt;T&gt;.Default

    这也是更适合 DI 的方法,因为您的 IConverter&lt;T&gt; 依赖于 T,而单个非泛型 IConverter 无法告诉 DI 要注入哪个特定实例。

    【讨论】:

      【解决方案4】:

      可以在你的泛型方法中做一些类型检查。

      public void Convert<T>(T value)
      {
          Console.WriteLine(typeof(T) == typeof(string) ? "String Method" : "Generic Method " + value);
      }
      

      但是,这样做会省略所有 generic 的定义,不是吗?话虽如此,不建议这样做。

      无论如何,仅仅因为两个方法具有相同的名称并不意味着它们相等的,因为编译器Convert(string)Convert&lt;T&gt;(T)是完全不同的方法。然而,该接口只知道通用接口,因此在转换为它时,根本不知道字符串方法。

      正如 Patrick 已经提到的那样,在接口上省略强制转换或实现字符串方法。另一个 - 也是肮脏的黑客 - 是将通用调用重定向到字符串方法:

      class Converter : IConverter
      {
          public void Convert<T>(T value)
          {
              if (typeof(T) == typeof(string) this.Convert((string) value);
              else Console.WriteLine("Generic Method " + value);
          }
      
          public void Convert(string value)
          {
              Console.WriteLine("String Method " + value);
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果你真的非常想保持界面不变,你可以使用类型检查:

        class Converter : IConverter
        {
            public void Convert<T>(T value)
            {
                if (value is string)
                {
                    Console.WriteLine("String Method " + (string)value);
                }
                else
                {
                    Console.WriteLine("Generic Method " + value);
                }
            }
        }
        

        或者,正如其他人所说,您可以将 Convert(string) 添加到 IConverter。

        【讨论】:

          猜你喜欢
          • 2013-03-16
          • 1970-01-01
          • 2011-03-14
          • 1970-01-01
          • 2012-03-01
          • 2017-11-13
          • 1970-01-01
          • 2013-03-09
          • 1970-01-01
          相关资源
          最近更新 更多