Возможно ли определить класс в C # так, чтобы
class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
Я не мог, хоть убей, сделать это прошлой ночью в .NET 3.5. Я пробовал использовать
delegate, Delegate, Action<T> and Func<T, T>
Мне кажется, что это должно быть как-то допустимо. Я пытаюсь реализовать свой собственный EventQueue.
Я закончил тем, что просто сделал это [примитивное приближение, заметьте].
internal delegate void DWork();
class EventQueue {
private Queue<DWork> eventq;
}
Но тогда я теряю возможность повторно использовать одно и то же определение для разных типов функций.
Мысли?
CA1065: Do not raise exceptions in unexpected locations
... Я всегда предполагал, что вы должны использовать настраиваемое правило анализа кода, чтобы найти недопустимые использования вашего класса, которые обычно недоступны во время выполнения.where T : Delegate
(и кто-то опубликовал новый ответ об этом ниже).Да , это возможно в C # 7.3, Ограничение семьи дополнилось
Enum
,Delegate
иunmanaged
типами. Вы можете без проблем написать этот код:void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged { }
Из Документов :
Полезные ссылки:
Будущее C # от Microsoft Build 2018
Что нового в C # 7.3?
источник
Изменить: некоторые предлагаемые обходные пути предлагаются в этих статьях:
http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
Из спецификации C # 2.0 мы можем прочитать (20.7, Ограничения):
Ограничение типа класса должно удовлетворять следующим правилам:
И, конечно же, VS2008 выдает ошибку:
error CS0702: Constraint cannot be special class 'System.Delegate'
Для получения информации и расследования по этой проблеме читайте здесь .
источник
Если вы хотите использовать зависимость времени компиляции от IL Weaver, вы можете сделать это с помощью Fody .
Использование этого дополнения к Fody https://github.com/Fody/ExtraConstraints
Ваш код может выглядеть так
public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } }
И быть скомпилированным
public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } }
источник
Делегат уже поддерживает цепочку. Разве это не соответствует вашим потребностям?
public class EventQueueTests { public void Test1() { Action myAction = () => Console.WriteLine("foo"); myAction += () => Console.WriteLine("bar"); myAction(); //foo //bar } public void Test2() { Action<int> myAction = x => Console.WriteLine("foo {0}", x); myAction += x => Console.WriteLine("bar {0}", x); myAction(3); //foo 3 //bar 3 } public void Test3() { Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; }; int y = myFunc(3); Console.WriteLine(y); //foo 3 //bar 3 //4 } public void Test4() { Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; Func<int, int> myNextFunc = x => { x = myFunc(x); Console.WriteLine("bar {0}", x); return x + 1; }; int y = myNextFunc(3); Console.WriteLine(y); //foo 3 //bar 5 //6 } }
источник
Я столкнулся с ситуацией, когда мне нужно было иметь дело с
Delegate
внутренним ограничением, но мне нужно было общее ограничение. В частности, я хотел добавить обработчик событий с использованием отражения, но я хотел использовать общий аргумент для делегата. Приведенный ниже код НЕ работает, поскольку "Handler" - это переменная типа, и компилятор не выполняет приведениеHandler
кDelegate
:public void AddHandler<Handler>(Control c, string eventName, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d); }
Однако вы можете передать функцию, которая выполняет преобразование за вас.
convert
принимаетHandler
аргумент и возвращаетDelegate
:public void AddHandler<Handler>(Control c, string eventName, Func<Delegate, Handler> convert, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d)); }
Теперь компилятор доволен. Вызвать метод очень просто. Например, присоединение к
KeyPress
событию в элементе управления Windows Forms:AddHandler<KeyEventHandler>(someControl, "KeyPress", (h) => (KeyEventHandler) h, SomeControl_KeyPress);
где
SomeControl_KeyPress
цель события. Ключом является лямбда-преобразователь - он не работает, но убеждает компилятор, что вы дали ему действительный делегат.(Начало 280Z28) @Justin: Почему бы не использовать это?
public void AddHandler<Handler>(Control c, string eventName, Handler d) { c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); }
(Конец 280Z28)
источник
Как упоминалось выше, вы не можете использовать Delegates и Enum в качестве общего ограничения.
System.Object
аSystem.ValueType
также не может использоваться в качестве общего ограничения.Обойти эту проблему можно, если вы создадите соответствующий вызов в своем IL. Будет работать нормально.
Вот хороший пример Джона Скита.
http://code.google.com/p/unconstrained-melody/
Я взял свои ссылки из книги Джона Скита C # in Depth , 3-е издание.
источник
Согласно MSDN
Ошибка компилятора CS0702
Ограничение не может быть идентификатором специального класса. Следующие типы не могут использоваться в качестве ограничений:
источник