Ограничение типа множественного типа (ИЛИ)

Чтение этого, я узнал, что можно позволить методу принимать параметры нескольких типов, сделав его общим методом. В этом примере следующий код используется с ограничением типа, чтобы гарантировать, что "U" - это IEnumerable<T>.

public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
    return arg.First();
}

Я нашел еще несколько кодов, которые позволили добавить несколько ограничений типа, например:

public void test<T>(string a, T arg) where T: ParentClass, ChildClass 
{
    //do something
}

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

public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}

Ваша помощь приветствуется, как всегда!

Ответ 1

Это невозможно. Однако вы можете определить перегрузки для определенных типов:

public void test(string a, string arg);
public void test(string a, Exception arg);

Если они являются частью общего класса, они будут предпочтительнее общей версии метода.

Ответ 2

Ответ Botz на 100% правильный, здесь короткое объяснение:

Когда вы пишете метод (общий или нет) и объявляете типы параметров, которые принимает метод, вы определяете контракт:

Если вы дадите мне объект, который знает, как делать множество вещей, которые Тип T знает, как это сделать, я могу доставить либо "a": возвращаемое значение тип я объявляет, или 'b': какое-то поведение, которое использует этот тип.

Если вы попытаетесь присвоить ему несколько типов за раз (с помощью or) или попытайтесь заставить его вернуть значение, которое может быть более одного типа, контракт становится нечетким:

Если вы дадите мне объект, который знает, как прыгать с веревкой или знает, как вычислить pi к 15-й цифре я верну либо объект, который может отправиться на рыбалку, или, может быть, смешать бетона.

Проблема в том, что когда вы попадаете в метод, вы не представляете, дали ли они вам IJumpRope или PiFactory. Кроме того, когда вы идете вперед и используете метод (при условии, что вы получили его для магической компиляции), вы не уверены, что у вас есть Fisher или AbstractConcreteMixer. В основном это все более запутывает.

Решение вашей проблемы является одной из двух возможностей:

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

  • Определите базовый класс или интерфейс, который знает, как выполнять все, что вам нужно для метода, и использовать один метод такого типа. Это может включать в себя обработку string и Exception в небольшом классе, чтобы определить, как вы планируете сопоставлять их с реализацией, но тогда все очень просто и легко читается. Я мог бы приехать через четыре года и прочитать ваш код и легко понять, что происходит.

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

Итак, для вашей конкретной ситуации я собираюсь представить, что вы просто вытаскиваете сообщение или что-то из этого исключения:

public interface IHasMessage
{
    string GetMessage();
}

public void test(string a, IHasMessage arg)
{
    //Use message
}

Теперь вам нужны только методы, которые преобразуют string и Exception в IHasMessage. Очень легко.

Ответ 3

Если ChildClass означает, что он получен из ParentClass, вы можете просто написать следующее, чтобы принять как ParentClass, так и ChildClass;

public void test<T>(string a, T arg) where T: ParentClass 
{
    //do something
}

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

public interface ICommonInterface
{
    string SomeCommonProperty { get; set; }
}

public class AA : ICommonInterface
{
    public string SomeCommonProperty
    {
        get;set;
    }
}

public class BB : ICommonInterface
{
    public string SomeCommonProperty
    {
        get;
        set;
    }
}

тогда вы можете написать свою общую функцию как:

public void Test<T>(string a, T arg) where T : ICommonInterface
{
    //do something
}