C# Generics не разрешает ограничения типа делегата


можно ли определить класс В 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;
}

но тогда я потеряю возможность использовать одно и то же определение для различных тип функции.

мысли?

8 71

8 ответов:

ряд классов недоступны в качестве общих противопоказаний-перечисление является другим.

для делегатов самое близкое, что вы можете получить, это": class", возможно, используя отражение, чтобы проверить (например, в статическом конструкторе), что T и делегат:

static GenericCollection()
{
    if (!typeof(T).IsSubclassOf(typeof(Delegate)))
    {
        throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
    }
}

Edit: некоторые предлагаемые обходные пути предлагаются в этих статьях:

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, ограничения):

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

  • тип должен быть типом класса.
  • тип не должен быть запечатан.
  • тип не должен быть одним из следующих типов: System.Массив, Система.Делегат, Система.Перечисление, или система.ValueType.
  • тип не должен быть object. Поскольку все типы являются производными от объекта, такое ограничение не будет иметь никакого эффекта, если оно будет разрешено.
  • не более одного ограничения для данного параметра типа может быть класс тип.

и конечно же VS2008 выплевывает ошибку:

error CS0702: Constraint cannot be special class 'System.Delegate'

для получения информации и расследования по этому вопросу читайте здесь.

Если вы хотите взять зависимость времени компиляции от IL Weaver, вы можете сделать это с помощью Фоды.

используя это дополнение к 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 до Delegate:

public void AddHandler<Handler>(Control c, string eventName, Handler d) {
  c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}

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

Да это возможно в 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?

Как упоминалось выше, вы не можете иметь делегаты и перечисление в качестве общего ограничения. System.Object и System.ValueType также не может использоваться в качестве общего ограничения.

работа вокруг может быть, если вы создадите соответствующий вызов в вас IL. Он будет работать нормально.

вот хороший пример Джона Скита.

http://code.google.com/p/unconstrained-melody/

Я взял свои рекомендации из книги Джона Скита C# в глубину, 3-е издание.

по данным MSDN

ошибка компилятора CS0702

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