【问题标题】:Specialize implementation of GenericType<A,B> for case A == B?为案例 A == B 专门实现 GenericType<A,B>?
【发布时间】:2009-04-03 03:27:32
【问题描述】:

我有一个泛型类,它接受两个类型参数Generic&lt;A, B&gt;。这个类的方法具有不同的签名,AB 是不同的。但是,如果A == B 签名完全匹配,则无法执行重载解析。是否有可能以某种方式为这种情况指定方法的专业化?还是强制编译器任意选择匹配的重载之一?

using System;

namespace Test
{
    class Generic<A, B>
    {
        public string Method(A a, B b)
        {
            return a.ToString() + b.ToString();
        }

        public string Method(B b, A a)
        {
            return b.ToString() + a.ToString();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Generic<int, double> t1 = new Generic<int, double>();
            Console.WriteLine(t1.Method(1.23, 1));

            Generic<int, int> t2 = new Generic<int, int>();
// Following line gives:
//     The call is ambiguous between the following methods
//     or properties: 'Test.Generic<A,B>.Method(A, B)' and
//     'Test.Generic<A,B>.Method(B, A)'
            Console.WriteLine(t2.Method(1, 2));   
        }
    }
}

【问题讨论】:

    标签: c# generics overloading


    【解决方案1】:

    鉴于纯泛型定义,无法强制编译器选择重载。它无法区分这两种方法的赢家。

    选择其中一个似乎是个好主意,但决定需要是确定性的。即使像文件中的第一个这样简单的东西也不是真正可行的,因为您必须考虑部分类。如果每个方法都在不同的文件中,编译器将如何选择第一种方法?

    您可以做的是添加接受 int 的方法的非泛型版本。编译器将选择非通用版本而不是通用版本,它会在这个非常有限的场景中产生胜利。但是,您必须对可能存在冲突的每种类型重复此操作。

    例如。添加此方法将解决您的编译错误,但仅适用于 int。

    public string Method(int b, int a)
    {
        return b.ToString() + a.ToString();
    }
    

    【讨论】:

      【解决方案2】:

      感谢您的好答案,他们促使我采用了这个解决方案:

      using System;
      
      namespace Test
      {
          class Generic<A, B>
          {
              public string Method(A a, B b)
              {
                  return this.DefaultMethod(a, b);
              }
      
              protected string DefaultMethod(A a, B b)
              {
                  return a.ToString() + b.ToString();
              }
      
              public string Method(B b, A a)
              {
                  return b.ToString() + a.ToString();
              }
          }
      
          class Generic<A> : Generic<A, A>
          {
              public new string Method(A a, A b)
              {
                  return base.DefaultMethod(a, b);
              }
          }
      
          class Program
          {
              static void Main(string[] args)
              {
                  Generic<int, double> t1 = new Generic<int, double>();
                  Console.WriteLine(t1.Method(1.23, 1));
      
                  Generic<int> t2 = new Generic<int>();
                  Console.WriteLine(t2.Method(1, 2));
              }
          }
      }
      

      【讨论】:

      • 不错的解决方案。没想到只创建两个泛型类 :)
      【解决方案3】:

      我知道这有点违背泛型的目的,但是如果定义一次方法,采用两个object 类型的参数呢?

      在方法内部,您可以检查类型并确定要调用两个选项中的哪一个。

      namespace Test
      {
          class Generic<A, B>
          {
              public string Method(object a, object b)
              {
                  if (a is A && b is B)
                      return MethodOneTwo;
                  else if (a is B && b is A)
                      return MethodTwoOne;
                  else
                      throw new ArgumentException("Invalid Types");
              }
      
              private string MethodOneTwo(A a, B b)
              {
                  return a.ToString() + b.ToString();
              }
      
              private string MethodTwoOne(B b, A a)
              {
                  return b.ToString() + a.ToString();
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        这将使用反射来获取方法并任意调用一个。您可以通过过滤获取方法时预期的参数和返回类型来使其更加健壮。

        using System;
        using System.Collections.Generic;
        using System.Text;
        
        namespace ConsoleApplication1
        {
            using System;
            using System.Reflection;
        
            namespace Test
            {
                class Generic<A, B>
                {
                    public string Method(A a, B b)
                    {
                        return a.ToString() + b.ToString();
                    }
        
                    public string Method(B b, A a)
                    {
                        return b.ToString() + a.ToString();
                    }
                }
        
                class Program
                {
                    static void Main(string[] args)
                    {
                        Generic<int, double> t1 = new Generic<int, double>();
                        Console.WriteLine(t1.Method(1.23, 1));
        
                        Generic<int, int> t2 = new Generic<int, int>();
                        // Following line gives:
                        //     The call is ambiguous between the following methods
                        //     or properties: 'Test.Generic<A,B>.Method(A, B)' and
                        //     'Test.Generic<A,B>.Method(B, A)'
                       MethodInfo [] methods = t2.GetType().GetMethods();
                        foreach(MethodInfo method in methods)
                        {
                            if (method.Name == "Method")
                            {
                                method.Invoke(t2,new Object[2] {1,2});
                                break;
                            }
                        }
                    }
                }
            }
        }
        

        编辑:这是一个关于您面临的问题的博客,其解决方案与 Jared 的类似。

        http://shiman.wordpress.com/2008/07/07/generic-method-overload-a-trap-for-c-net-library-developers/

        我们真正需要的是在预编译或编译时生成具体签名的模板。

        【讨论】:

          【解决方案5】:

          没有。

          如果你想让编译器任意决定,你调用方法的目的是什么?

          【讨论】:

          • 匹配的重载之一,不是一些随机方法。两个匹配的重载必须具有相同的行为,否则该类无论如何都会被破坏。
          猜你喜欢
          • 2018-11-22
          • 2014-03-29
          • 2021-10-06
          • 1970-01-01
          • 1970-01-01
          • 2021-11-28
          • 1970-01-01
          • 1970-01-01
          • 2010-12-08
          相关资源
          最近更新 更多