Как заставить метод взять 2 разных типа - PullRequest
4 голосов
/ 10 сентября 2010

Я знаю, что это, вероятно, очень простой вопрос, но у меня сейчас пукнет мозг. Я пытаюсь создать метод, который может принимать один из 2 пользовательских типов. По сути, тело этого метода будет идентичным для обоих типов, так как они оба имеют свойство Name (я сравниваю свойство Name для использования при сортировке). Как мне это сделать?

Моей первой мыслью было просто перегрузить метод двумя типами в качестве аргументов:

int Compare(Type1 first, Type1 second)
int Compare (Type2 first, Type2 second)

но основная часть методов оказалась идентичной, поэтому это кажется пустой тратой.

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

Пояснение: «пользовательские» типы на самом деле не являются моими пользовательскими типами. Я имел в виду, что они не встроенные типы. У меня нет контроля над тем, что находится в этих типах или иерархии наследования. Просто у них обоих есть свойство Name.

Ответы [ 7 ]

8 голосов
/ 10 сентября 2010

Как ни странно, до сих пор никто не опубликовал то, что мне кажется очевидным: в качестве аргумента сравнения взять имя . Заставьте caller получить свойство name из объекта. Если все, что нужно использовать в методе - это имя, то почему вы передаете остальную часть объекта?

Если вам нужно абстрагироваться от понятия «я могу получить имя из этой вещи», то сделайте это:

Пример:

int Compare(string name1, string name2) { ... whatever ... }
int Compare<T>(T t1, T t2, Func<T, string> getName)
{
    return Compare(getName(t1), getName(t2));
}

И теперь вы можете сказать:

Compare(car1, car2, c=>c.Name);
Compare(employee1, employee2, e=>e.Name);

и т. Д.

7 голосов
/ 10 сентября 2010

Generic по-прежнему является опцией, если два типа происходят от одной и той же базы или используют один и тот же интерфейс, и никакие другие классы не используют базу и / или интерфейс.

int Compare<T>(T first, T second) where T : IHasName

Если не считать,путь к двум перегрузкам.

Примечание. Если у вас есть .NET 4, вы можете сделать его исполняемым и сделать метод динамическим.

int Compare(dynamic first, dynamic second)

В этом случае все, что вы кидаете в него, скомпилируется, но оно взорвется во время выполнения, если свойство Name отсутствует.

Лично, если я не могу изменить тип для использования общего интерфейса, то я бы пошел с перегрузками и получил бы безопасность во время компиляции.

4 голосов
/ 10 сентября 2010

Хорошо, я прочитал ваш вопрос совершенно неправильно в первом ответе. Вот лучше:)

Перегрузки - ваш лучший выбор. Поскольку результат сравнения зависит только от Name, создайте метод, который делает это сравнение, а затем вызовите его:

private int Compare(string first, string second)
{
    // do comparison
}

public int Compare(Type1 first, Type1 second)
{
    return Compare(first.Name, second.Name):
}

public int Compare(Type2 first, Type2 second)
{
    return Compare(first.Name, second.Name);
}

Edit:

Поскольку у вас есть несколько предметов, вы можете сделать две вещи:

public int Compare(string first1, string second1, X first2, X second2 ...

Но это будет немного уродливо. Альтернативой является предоставление прогноза для извлечения значений:

private int Compare<T>(T first, T second, Func<T,Tuple<string,int,TypeX,TypeY>> projection)
{
    // test for first==null, second==null, etc...

    var nicerFirst = projection(first);
    var nicerSecond = projection(second);

    // compare objects using nicerFirst.Item1, nicerFirst.Item2, etc.
}

Тогда ваше сравнение выглядит примерно так:

int Compare(Type1 first, Type1 second)
{
    return Compare(first, second, x => Tuple.Create(x.Name, x.Int, x.X, x.Y));
}
1 голос
/ 10 сентября 2010

Перегрузка метода была бы хорошим способом заставить его принимать только два конкретных типа.Чтобы не повторять код, вы можете использовать обобщенные сценарии:

public int Compare(Type1 first, Type1 second) {
  return Compare<Type1>(first, second);
}

public int Compare(Type2 first, Type2 second) {
  return Compare<Type2>(first, second);
}

private int Compare<T>(T first, T second) {
  ...
}
1 голос
/ 10 сентября 2010

Если он может только использоваться с 2 типами, то сделать 2 перегрузки.

0 голосов
/ 10 сентября 2010

Это, вероятно, решение за бортом, но вы могли бы сделать адаптеры для двух классов и заставить их реализовать общий интерфейс:

public class MyType1 : ICommonInterface, Type1
{
    // Where you can define 'Somethin' in the common interface
    public string Something 
    { 
        get { return base.Something; } 
        set { base.Something = value; } 
    }

     /* ... */
}

public class MyType2 : ICommonInterface, Type2
{
    // If there is no base.Something on Type2, you can re-name it assuming 
    // its intent is the same as Something
    public string Something
    {
        get { return base.SomethingElse; } 
        set { set base.SomethingElse = value; }
    }        
}

Тогда вы можете реализовать в основном именно то, что @Anthony Pegram (+1) имел:

int Compare<T> (T first, T second) where T : ICommonInterface
{
    return first.Something.CompareTo(second.Something);
}
0 голосов
/ 10 сентября 2010

Возможно, это не лучшее решение, но вот что мне приходит в голову: вы делаете метод вроде:

int MainCompare(Type1 first1, Type1 second1, Type2 first2, Type2 second2, bool firsttype){...}

Тогда вы можете использовать два метода сравнения в одной строке.

int compare(Type1 first, Type1 second){
    return MainCompare(first, second, null, null, true);
}


int compare(Type2 first, Type2 second){
    return MainCompare(null, null, first, second, false);
}

ваш MainCompare будет немного меняться в зависимости от того, какой тип он использует.

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