C # Pass Generics во время выполнения - PullRequest
9 голосов
/ 09 апреля 2010

У меня есть метод, подобный следующему:

public IEnumerable<T> GetControls<T>()
 : where T : ControlBase
{
 // removed.
}

Затем я создал класс:

public class HandleBase<TOwner> : ControlBase
 : TOwner
{
 // Removed
}

Я бы хотел позвонить

GetControls<HandleBase<this.GetType()>>; 

где будет использоваться тип ЭТОГО класса для передачи в HandleBase. Это, по сути, получит все HandleBase, которые имеют владельца этого типа.

Как мне этого добиться?

EDIT:

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

Идея состоит в том, чтобы ControlBase имел коллекцию других ControlBase для "потомков". Затем их можно запрашивать в зависимости от их типа с помощью GetControls<T>(). Это позволило бы мне, например, получить всю HandleBase для Shape. Затем я могу взять все это и установить Visible = false или сделать что-то еще с ними. Таким образом, я могу манипулировать детьми определенного типа для коллекции.

HandleBase<TOwner> требует TOwner, так как он имеет ссылку на «тип владения». Таким образом, вы можете добавить в Shape только все, что расширяет HandleBase. Имеет смысл?

Спасибо за помощь!

Ответы [ 6 ]

13 голосов
/ 09 апреля 2010

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

Вы можете сделать это с помощью отражения следующим образом:

typeof(SomeClass).GetMethod("GetControls")
    .MakeGenericMethod(typeof(HandleBase<>).MakeGenericType(GetType()))
    .Invoke(someObject, null);

Обратите внимание, что он вернет object; вы не сможете привести его к IEnumerable<T> (если вы не знаете, что такое T во время компиляции, и в этом случае нет никакого смысла). Вы могли бы привести его к IEnumerable.

Однако это плохая идея.
Возможно, есть лучшее решение для вас; пожалуйста, предоставьте более подробную информацию.

2 голосов
/ 09 апреля 2010

Вы не можете. Generics - это функция времени компиляции. Вам нужно будет включить тип как неуниверсальный параметр в метод и передать его туда.

1 голос
/ 09 апреля 2010

Обратите внимание, что параметры типа не являются переменными. Следовательно, вы не можете использовать переменную вместо параметра типа.

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

public class MyClass<TSelf> where TSelf: MyClass<TSelf> {
    public IEnumerable<T> GetControls<T>() where T: ControlBase {
     // removed.
    }

    public void MyCall() {
        GetControls<HandleBase<TSelf>>(); 
    }
}

public class MyConcreteClass: MyClass<MyConcreteClass> {
}
0 голосов
/ 09 апреля 2010

Это специфично для моей реализации этого, но я смог решить эту проблему, создав сначала неуниверсальный HandleBase, а затем универсальный HandleBase<TOwner>, поскольку единственным местом, где использовался TOwner, было свойство Owner.

Тогда, когда я могу позвонить GetControls<HandleBase> и получить все HandleBase независимо от владельца.

Спасибо всем за ответы!

0 голосов
/ 09 апреля 2010

Поскольку GetControls () возвращает перечисление, вы можете найти способ отфильтровать полученное перечисление с помощью .OfType , что-то вроде

List<T2> list = controlList.GetControls<T>().OfType<T2>().ToList();

Вам понадобится общее ограничение, как-то вроде

where T2 : T
0 голосов
/ 09 апреля 2010

Вероятно, нет способа сделать то, что вы просите. Расширяем ваш пример:

X x = GetControls<HandleBase<this.GetType()>>; 

Каким должен быть тип X здесь? Однако из другой справочной информации кажется, что вам нужно получить список всех элементов управления данного типа. Например, вы можете сделать это следующим образом:

public IEnumerable<ControlBase> GetControls(Type type) {
//your logic here
}

В зависимости от других ваших целей и задач вы также можете вернуть неуниверсальный IEnumerable.

...