Специализировать реализацию GenericType <A, B> для случая A == B? - PullRequest
0 голосов
/ 03 апреля 2009

У меня есть универсальный класс, который принимает два параметра типа, Generic<A, B>. Этот класс имеет методы с сигнатурами, которые различаются так долго, а A и B различны. Однако, если 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));   
        }
    }
}

Ответы [ 5 ]

3 голосов
/ 03 апреля 2009

Учитывая чисто общее определение, невозможно заставить компилятор выбрать перегрузку. Невозможно отличить победителя между этими двумя методами.

Может показаться хорошей идеей просто выбрать одно или другое, но решение должно быть детерминированным. Даже такая простая вещь, как первая в файле, на самом деле не выполнима, потому что вы должны учитывать частичные классы. Как бы компилятор выбрал первый метод, если бы каждый был в отдельном файле?

Что вы можете сделать, это добавить неуниверсальную версию метода, которая принимает int. Компилятор выберет неуниверсальную версию по сравнению с общей версией и в этом очень ограниченном сценарии получит победу. Вы должны будете повторить это для каждого типа, который может иметь конфликт, хотя.

Например. Добавление этого метода решит вашу ошибку компиляции, но только для int.

public string Method(int b, int a)
{
    return b.ToString() + a.ToString();
}
2 голосов
/ 03 апреля 2009

Спасибо за хорошие ответы, они подсказали мне это решение:

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));
        }
    }
}
1 голос
/ 03 апреля 2009

Я знаю, что это в некоторой степени побеждает цель общего, но как насчет определения метода один раз, принимая два параметра типа 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();
        }
    }
}
0 голосов
/ 03 апреля 2009

Это будет использовать отражение, чтобы получить методы и произвольно вызвать один. Вы можете сделать это более надежным, отфильтровав параметры и возвращая типы, которые ожидаются, когда вы получаете методы.

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

Редактировать: вот блог о проблеме, с которой вы столкнулись, с решением, похожим на решение Джареда.

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

Что нам действительно нужно, так это шаблоны, которые генерируют конкретные подписи при прекомпиляции или во время компиляции.

0 голосов
/ 03 апреля 2009

номер

Если вы хотите, чтобы компилятор решал все произвольно, какова цель вызова метода?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...