C # Generics는 위임 형식 제약 조건을 허용하지 않습니다.


79

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;
}

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

생각?

답변:


66

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

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

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

8
+1 : 1) 정적 생성자 사용 및 2) 유형 초기화를 둘러싼 이상한 디버깅 조건으로 인한 자세한 메시지 포함.
Sam Harwell

6
@MarcGravell : 정적 이니셜 라이저 위반에서 예외를 던지지 CA1065: Do not raise exceptions in unexpected locations않습니다. 일반적으로 런타임에 사용할 수없는 클래스의 잘못된 사용을 찾기 위해 사용자 지정 코드 분석 규칙을 사용해야한다는 가정하에 항상있었습니다.
myermian 2012

3
C # 7.3 (2018 년 5 월 릴리스)부터는 다음과 같이 제한 할 수 있습니다. where T : Delegate, (누군가가 이에 대한 새로운 답변을 게시했습니다).
Jeppe Stig Nielsen

16

예, 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의 새로운 기능은 무엇입니까?


예, C # 7.3 (2018 년 5 월 이후)에서 가능하며 여기에서 릴리스 정보를 볼 수 있습니다 .
Jeppe Stig Nielsen

13

편집 : 다음 문서에서는 몇 가지 제안 된 해결 방법을 제안합니다.

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'

이 문제에 대한 정보 및 조사는 여기를 참조하십시오 .


10

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
    {
    }
}

끊어진 링크. 현재 가지고 있습니까?
Justin Morgan

3

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
    }

}

그게 정말 내가 찾고 있어요 기능 ... 난 ... 내 제네릭 클래스에 타입 제약 조건을 만들려고하지
니콜라스 맨큐

3

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)


1
@Justin : 코드 블록이 있기 때문에 끝에 내 의견을 넣도록 답변을 편집했습니다.
Sam Harwell

2

위에서 언급했듯이 Delegates 및 Enum을 일반 제약 조건으로 사용할 수 없습니다. System.Object그리고 System.ValueType또한 일반적인 제약 조건으로 사용할 수 없습니다.

IL에서 적절한 호출을 구성하면 해결 방법이 될 수 있습니다. 잘 작동합니다.

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

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

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


1

MSDN 에 따르면

컴파일러 오류 CS0702

제약 조건은 특수 클래스 '식별자'일 수 없습니다. 다음 유형은 제약 조건으로 사용할 수 없습니다.

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.

여기서 질문을 반복하는 이유는 무엇입니까? 당신은 우리에게 새로운 것을 말하지 않습니다.
Elmue
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.