Вопрос по c#, types, asp.net-mvc-3 – Типовое ограничение множественного (ИЛИ) типа

107

чтениеэтотЯ узнал, что можно позволить методу принимать параметры нескольких типов, сделав его универсальным методом. В этом примере следующий код используется с ограничением типа, чтобы обеспечить "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 and ChildClass, Я хочу сказать, что arg может быть типомParentClass or ChildClass следующим образом:

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

Ваша помощь ценится как всегда!

Но когда нет никакой связи, вы можете вызывать только те методыarg те, которые определеныobject - так почему бы просто не удалить дженерики с картинки и сделать типarg object? Что вы получаете? Damien_The_Unbeliever
Также обратите внимание, что в письменной форме нет смыслаwhere T : string, какstring этоsealed учебный класс. Единственная полезная вещь, которую вы могли бы сделать, это определить перегрузки дляstring а такжеT : Exceptionкак объяснил @ Botz3000 в своем ответе ниже. Mattias Buelens
@Mansfield Вы могли бы сделатьprivate метод, который принимает параметр объекта. Обе перегрузки назвали бы это. Здесь не нужны дженерики. Botz3000
@Damien_The_Unbeliever Не уверен, что вы подразумеваете под телом? Если вы не имеете в виду разрешение какого-либо типа и ручную проверку в теле ... и в реальном коде, который я хочу написать (последний фрагмент кода), я хотел бы иметь возможность передавать строковое ИЛИ исключение, поэтому класс отсутствует отношения есть (кроме system.object я представляю). Mansfield
Что вы могли бы с пользой сделать,in a generic manner в теле такого метода (если только несколько типов не являются производными от определенного базового класса, в таком случае, почему бы не объявить это как ограничение типа)? Damien_The_Unbeliever

Ваш Ответ

3   ответа
24

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

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

If you give me an object that knows how to do the set of things that Type T knows how to do I can deliver either 'a': a return value of the type I declare, or 'b': some sort of behavior that uses that type.

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

If you give me an object that knows how to jump rope or knows how to calculate pi to the 15th digit I'll return either an object that can go fishing or maybe mix concrete.

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

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

  1. Define more than one method that defines each possible transformation, behavior, or whatever. That's Botz's answer. In the programming world this is referred to as Overloading the method.

  2. Define a base class or interface that knows how to do all the things that you need for the method and have one method take just that type. This may involve wrapping up a string and Exception in a small class to define how you plan on mapping them to the implementation, but then everything is super clear and easy to read. I could come, four years from now and read your code and easily understand what's going on.

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

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

public interface IHasMessage
{
    string GetMessage();
}

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

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

извините @ Botz3000, только что заметил, что я неправильно написал ваше имя.
@ Алекс, но это не то, что делает C #.
Дело в том, что ограничения универсальных параметров C # и сами шаблоны являются довольно примитивными по сравнению, скажем, с шаблонами C ++. C # требует, чтобы вы сообщали компилятору заранее, какие операции разрешены для универсальных типов. Чтобы предоставить эту информацию, нужно добавить ограничение интерфейса (где T: IDisposable). Но вы можете не захотеть, чтобы ваш тип реализовывал некоторый интерфейс для использования универсального метода, или вы можете разрешить некоторым типам в вашем универсальном коде, которые не имеют общего интерфейса. Ex. разрешите любую структуру или строку, чтобы вы могли просто вызвать Equals (v1, v2) для сравнения на основе значений.
Это правда, но это возможно. Я прочитал этот ответ как то, что он не сможет, я неправильно истолковал?
Или компилятор может угрожать типу как типу объединения внутри функции и иметь возвращаемое значение того же типа, что и в нем. TypeScript делает это.
51

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

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

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

Интересно. Это произошло со мной, но я подумал, что это может сделать код чище, чтобы использовать одну функцию. Ах, хорошо, большое спасибо! Просто из любопытства, знаете ли вы, есть ли конкретная причина, по которой это невозможно? (было ли это намеренно исключено из языка?) Mansfield
@Mansfield Я не знаю точной причины, но я думаю, что вы больше не сможете использовать общие параметры осмысленным образом. Внутри класса они должны были бы рассматриваться как объект, если бы им было позволено иметь совершенно разные типы. Это означает, что вы также можете опустить универсальный параметр и обеспечить перегрузки.
@Mansfield, это потому чтоor отношения делают вещи далеко от общего, чтобы быть полезным. Вы & APOS; дhave сделать отражение, чтобы выяснить, что делать и все такое. (YUCK!).
5

Если 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
}
Фактически я пишу простую функцию регистрации ошибок. Мне бы хотелось, чтобы этот последний параметр был либо строкой информации об ошибке, либо исключением, и в этом случае я в любом случае сохраняю e.message + e.stacktrace как строку. Mansfield
это проблема дизайна на самом деле. универсальная функция используется для выполнения аналогичных операций с повторным использованием кода. если вы планируете выполнять различные операции в теле метода, лучше использовать разделение методов (IMHO).
Вы можете написать новый класс, имеющий isSuccesful, сохранить сообщение и исключение в качестве свойств. тогда вы можете проверить, является ли выдаваемое значение true, и сделать оставшееся.
Хорошая идея, за исключением того, что я не думаю, что смогу сделать это, поскольку я хочу использовать строку, которая является запечатанным классом согласно комментарию выше ... Mansfield

Похожие вопросы