Пусть метод принимает любой тип данных в c # - PullRequest
25 голосов
/ 04 мая 2011

У меня есть много модульных тестов, которые в значительной степени проверяют то же поведение.Однако тип данных изменяется.

Я пытаюсь создать универсальный метод, который может принимать данные любого типа.Я попытался сделать мой входной параметр var, но это не разрешено.Кроме того, изучил дженерики c #, но обычно это касается списка.

Ответы [ 5 ]

51 голосов
/ 04 мая 2011

Вы можете сделать параметр object:

public void DoSomething(object arg)
{
   //...

Или вы можете сделать то, что я предпочитаю, и сделать общий метод:

public void DoSomething<T>(T arg)
{
    //...

Общий подход имеет два основных преимущества, и я приведу примеры их полезности:

  1. Даже если вы явно не указали тип arg, у вас все равно есть к нему доступ.
  2. Вы можете добавить ограничения на типы, которые вы хотите разрешить.

И наоборот, подход object имеет ряд важных недостатков:

  1. Поскольку вы воспринимаете arg как object, вы сможете делать только то, что можете сделать с любым объектом.
  2. Если вы передадите тип значения в качестве параметра object, переменная будет в штучной упаковке , что означает снижение производительности. Это не огромный удар, но если вы звоните DoSomething несколько тысяч раз подряд, вы можете начать чувствовать это.

Обобщения и типовые ограничения

Добавление ограничения типа к универсальному методу позволяет ограничить метод так, чтобы он принимал только определенные типы. Почему это полезно? Потому что даже если вы не знаете или не заботитесь о том, с каким конкретным типом вы работаете, теперь вы кое-что знаете об этом и можете использовать эту информацию.

Рассмотрим следующую настройку:

public interface IAnimal 
{ 
    void Move(); 
}
public class Duck : IAnimal
{
    public void Move() 
    { 
        Console.WriteLine("Flying"); 
    }
}
public class Fish : IAnimal
{
    public void Move()
    { 
        Console.WriteLine("Swimming"); 
    }
}
public class Ant : IAnimal
{
    public void Move()
    { 
        Console.WriteLine("Walking"); 
    }
}    

Поскольку у нас есть IAnimal интерфейс, мы можем написать универсальные методы для любой реализации IAnimal:

public class Program
{
    static void DoMove<T>(T animal) where T : IAnimal
    {
        animal.Move();
    }
    public static void Main(string[] args)
    {            
        Duck duck = new Duck(); 
        Fish fish = new Fish();
        Ant ant = new Ant(); 

        DoMove<Duck>(duck);
        DoMove<Fish>(fish);
        DoMove<Ant>(ant);
    }
}

Запустите его: http://rextester.com/GOF1761

Когда мы пишем метод DoMove, нам все равно, является ли его параметр animal Duck, Fish, Ant или чем-то еще. Все, что нас волнует, это звонить animal.Move(). Поскольку мы использовали ограничение where T : IAnimal, компилятор знает все, что нам нужно знать:

  1. Переменная animal имеет тип T.
  2. Что бы ни было T, оно реализует IAnimal.
  3. Все, что реализует IAnimal, имеет метод Move().
  4. Поэтому можно смело звонить animal.Move().

(Кстати, да, мы могли бы просто написать DoMove как static void DoMove(IAnimal animal), но это другое обсуждение.)

Вывод типа (и некоторые его последствия)

Хорошо, но давайте сделаем еще один шаг. Во многих случаях вы можете вызывать универсальные методы без указания параметров их типа. Это называется вывод типа , и, кроме того, что вы экономите время на вводе, это может быть полезно при выполнении одной и той же операции над объектами разных типов.

public static void Main(string[] args)
{            
    IAnimal[] animals = new IAnimal[] 
    {
        new Duck(),
        new Fish(),
        new Ant()
    };

    foreach (IAnimal animal in animals)
    {
        DoMove(animal);
    }
}

Запустите его: http://rextester.com/OVKIA12317

Вам нужно всего лишь написать метод DoMove<T> один раз, и вы можете вызывать его для любого типа IAnimal без указания более конкретного типа. Соответствующая версия Move будет вызываться каждый раз, потому что DoMove<T> может определить, какой тип использовать для T. Когда вы вызываете DoMove(duck), .NET понимает, что вы действительно имеете в виду DoMove<Duck>(duck), который затем вызывает метод Move в классе Duck.

8 голосов
/ 04 мая 2011

Вы можете принять object в качестве типа параметра. Возможно, даже лучше было бы использовать дженерики:

void MyMethod<T>(T parm) { ... }

Таким образом, параметр на самом деле относится к типу, который передал пользователь - он не упакован как object и типы значений.

4 голосов
/ 04 мая 2011
void MyTestMethod<T>(T t) { }

дает вам общий метод тестирования, но я не могу представить какой-либо способ, который может быть полезен.Что нужно проверить?Как вы знаете, тип T имеет эти методы?T может быть любым типом в вышеуказанном методе.Единственные методы, которые вы можете вызвать из t в приведенном выше примере, - это общие методы object.

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

interface IMyInterface
{
    void DoSomething();
} 

void MyTestMethod<T>(T t) where T : IMyInterface
{ 
    t.DoSomething();
}
3 голосов
/ 04 мая 2011
public void YourMethod<T>(T parameter)
{
}
0 голосов
/ 04 мая 2011

попробуйте использовать динамическое ключевое слово, это будет работать при условии, что все ваши различные типы имеют одинаковые методы, которые используются вашими модульными тестами, в противном случае вы получите исключение времени выполнения

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