Как создать универсальный класс с универсальным использованием известного типа в качестве параметра?

#c#

#c#

Вопрос:

Допустим, у меня есть класс, не являющийся универсальным NonGenericClass , и я хотел бы иметь универсальный класс GenericClass<T> , метод которого возвращал T<NonGenericClass> бы .

Пример кода Pseduo:

 public class NonGeneric Class {}

public class GenericClass<T>  
{
    public T<NonGenericClass> foo()
    {
        return new T<NonGenericClass>();
    } 
}
  

Как я мог бы выполнить это на c #?

Комментарии:

1. T<NonGenericClass> не имеет никакого смысла. Либо вы не знаете, поэтому используете T , либо вы знаете и используете NonGenericClass

2. Через систему ограничений невозможно выразить «этот параметр типа должен быть открытым универсальным типом с арностью 1», что и предполагает ваш код в отношении параметра типа.

3. У вас есть универсальный класс, который имеет NonGenericClass общий тип?

4. Вам нужно было бы добавить ограничение на T то, что это универсальный интерфейс или класс, который принимает NonGenericClass в качестве типа, но вы бы этого не сделали T<NonGenericClass> . Например, вы могли бы иметь where T : IEnumberable<NonGenericClass> , но вы все равно просто использовали T бы в качестве возвращаемого типа. Какую актуальную проблему вы пытаетесь решить, поскольку это похоже на проблему XY .

5. Я называю это — без унижения — общей Кроличьей норой безумия . Я думаю, мы все это сделали. Если мы пытаемся решить эту проблему, то маловероятно, что мы написали какой-либо код, который использует то, что мы пытаемся выяснить, как сделать. Гораздо лучше просто написать нужный нам код без обобщений, а затем посмотреть, показывает ли он что-то, что мы можем упростить с помощью обобщений.

Ответ №1:

 public class NonGenericClass {}

public class OtherGenericClass<T>{}

public class GenericClass<T,T2>
    where T: OtherGenericClass<T2>, new()
    where T2: NonGenericClass
{
    public T Foo()
    {
        return new T();
    }
}
  

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

Это означает, что Foo() всегда будет возвращаться OtherGenericClass<T2> и T2 всегда будет NonGenericClass .

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

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

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

new() Ограничение связано только с тем, что метод возвращает new T() . Он не будет компилироваться, если только без этого ограничения, которое указывает, что T() это должно быть что-то, что имеет конструктор без аргументов.