[c#] 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;
}

그러나 다른 유형의 기능에 대해 동일한 정의를 재사용 할 수있는 능력을 잃습니다.

생각?



답변

일반 제약으로 사용할 수없는 클래스가 많이 있습니다. Enum이 다른 것입니다.

델리게이트의 경우 가장 가까운 “: class”는 T 델리게이트 인지 확인하기 위해 (예 : 정적 생성자에서) 리플렉션을 사용합니다 .

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


답변

예, C # 7.3에서 가능하며 제약 조건 제품군은 Enum, Delegateunmanaged형식 을 포함하도록 증가되었습니다 . 이 코드는 문제없이 작성할 수 있습니다.

void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
    {

    }

문서에서 :

C # 7.3부터는 관리되지 않는 제약 조건을 사용하여 형식 매개 변수가 nullable이 아닌 관리되지 않는 형식이어야 함을 지정할 수 있습니다. 관리되지 않는 제약 조건을 사용하면 메모리 블록으로 조작 할 수있는 유형으로 작업하는 재사용 가능한 루틴을 작성할 수 있습니다.

유용한 링크:

Microsoft Build 2018 의 C # 미래

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, 제약)를 읽을 수 있습니다 :

클래스 유형 제약 조건은 다음 규칙을 충족해야합니다.

  • 유형은 클래스 유형이어야합니다.
  • 유형은 봉인되지 않아야합니다.
  • 형식은 System.Array, System.Delegate, System.Enum 또는 System.ValueType 중 하나가 아니어야합니다 .
  • 유형은 객체가 아니어야합니다. 모든 유형이 객체에서 파생되기 때문에 이러한 제약 조건은 허용되는 경우 효과가 없습니다.
  • 주어진 유형 매개 변수에 대한 최대 하나의 제약 조건은 클래스 유형이 될 수 있습니다.

그리고 충분히 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
    {
    }
}


답변

Delegate는 이미 연결을 지원합니다. 이것이 귀하의 요구를 충족하지 않습니까?

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취하고 다음 Handler을 반환합니다 Delegate.

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

이제 컴파일러는 행복합니다. 메서드를 호출하는 것은 쉽습니다. 예를 들어 KeyPressWindows 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에서 적절한 호출을 구성하면 해결 방법이 될 수 있습니다. 잘 작동합니다.

다음은 Jon Skeet의 좋은 예입니다.

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

나는 Jon Skeet의 저서 C # in Depth , 3rd edition 에서 참조를 가져 왔습니다 .