다음을 통해 공유


C#에서의 대리자 변동

.NET Framework 3.5에서는 C#의 모든 대리자에서 대리자 형식과 메서드 시그니처를 일치시키는 분산 지원을 도입했습니다. 즉, 일치하는 서명이 있는 메서드뿐만 아니라 더 많은 파생 형식(공변성)을 반환하거나 대리자 형식에서 지정한 것보다 파생 형식(반공변성)이 적은 매개 변수를 허용하는 메서드에도 할당할 수 있습니다. 여기에는 제네릭 대리자와 제네릭이 아닌 대리자가 모두 포함됩니다.

예를 들어 제네릭과 제네릭이 아닌 두 개의 클래스와 두 개의 대리자가 있는 다음 코드를 고려해 보세요.

public class First { }  
public class Second : First { }  
public delegate First SampleDelegate(Second a);  
public delegate R SampleGenericDelegate<A, R>(A a);  

또는 SampleDelegate 형식의 SampleGenericDelegate<A, R> 대리자를 만들 때 다음 메서드 중 하나를 해당 대리자에게 할당할 수 있습니다.

// Matching signature.  
public static First ASecondRFirst(Second second)  
{ return new First(); }  
  
// The return type is more derived.  
public static Second ASecondRSecond(Second second)  
{ return new Second(); }  
  
// The argument type is less derived.  
public static First AFirstRFirst(First first)  
{ return new First(); }  
  
// The return type is more derived
// and the argument type is less derived.  
public static Second AFirstRSecond(First first)  
{ return new Second(); }  

다음 코드 예제에서는 메서드 서명과 대리자 형식 간의 암시적 변환을 보여 줍니다.

// Assigning a method with a matching signature
// to a non-generic delegate. No conversion is necessary.  
SampleDelegate dNonGeneric = ASecondRFirst;  
// Assigning a method with a more derived return type
// and less derived argument type to a non-generic delegate.  
// The implicit conversion is used.  
SampleDelegate dNonGenericConversion = AFirstRSecond;  
  
// Assigning a method with a matching signature to a generic delegate.  
// No conversion is necessary.  
SampleGenericDelegate<Second, First> dGeneric = ASecondRFirst;  
// Assigning a method with a more derived return type
// and less derived argument type to a generic delegate.  
// The implicit conversion is used.  
SampleGenericDelegate<Second, First> dGenericConversion = AFirstRSecond;  

자세한 예제는 대리자에서 분산 사용(C#)Func 및 Action 제네릭 대리자(C#)에 대한 분산 사용(C#)을 참조하세요.

제네릭 형식 매개 변수의 분산

.NET Framework 4 이상에서는 대리자 간에 암시적 변환을 사용하도록 설정할 수 있으므로 제네릭 형식 매개 변수로 지정된 형식이 다른 제네릭 대리자를 서로 할당할 수 있습니다( 분산에 필요한 경우 형식이 서로 상속되는 경우).

암시적 변환을 사용하도록 설정하려면 대리자의 제네릭 매개 변수를 키워드 `in` 또는 `out`을 사용하여 공변 또는 반공변으로 명시적으로 선언해야 합니다.

다음 코드 예제에서는 공변 제네릭 형식 매개 변수가 있는 대리자를 만드는 방법을 보여줍니다.

// Type T is declared covariant by using the out keyword.  
public delegate T SampleGenericDelegate <out T>();  
  
public static void Test()  
{  
    SampleGenericDelegate <String> dString = () => " ";  
  
    // You can assign delegates to each other,  
    // because the type T is declared covariant.  
    SampleGenericDelegate <Object> dObject = dString;
}  

변수 지원만 사용하여 메서드 시그니처를 대리자 형식과 일치시키고 c0 및 c1 키워드를 사용하지 않는 경우, 때로는 동일한 람다 식 또는 메서드로 대리자의 인스턴스를 생성할 수는 있지만, 한 대리자를 다른 대리자에게 할당할 수는 없습니다.

다음 코드 예제에서 SampleGenericDelegate<String>SampleGenericDelegate<Object>을 상속받았지만 String로 명시적으로 변환될 수는 없습니다. 제네릭 매개 변수 T 를 키워드로 out 표시하여 이 문제를 해결할 수 있습니다.

public delegate T SampleGenericDelegate<T>();  
  
public static void Test()  
{  
    SampleGenericDelegate<String> dString = () => " ";  
  
    // You can assign the dObject delegate  
    // to the same lambda expression as dString delegate  
    // because of the variance support for
    // matching method signatures with delegate types.  
    SampleGenericDelegate<Object> dObject = () => " ";  
  
    // The following statement generates a compiler error  
    // because the generic type T is not marked as covariant.  
    // SampleGenericDelegate <Object> dObject = dString;  
  
}  

.NET에 변형 형식 매개 변수가 있는 제네릭 대리자

.NET Framework 4에는 여러 기존 제네릭 대리자에서 제네릭 형식 매개 변수에 대한 분산 지원이 도입되었습니다.

자세한 내용 및 예제는 Func 및 Action 제네릭 대리자(C#)에 대한 분산 사용을 참조하세요.

제네릭 대리자에서 변형 형식 매개변수 선언

제네릭 대리자가 공변 또는 반공변 제네릭 형식 매개 변수를 갖는 경우 변형 제네릭 대리자라고 할 수 있습니다.

키워드 out를 사용하여 제네릭 대리자에서 제네릭 형식 매개 변수의 공변성을 선언할 수 있습니다. 공변 형식은 메서드 반환 형식으로만 사용할 수 있으며 메서드 인수의 형식으로는 사용할 수 없습니다. 다음 코드 예제에서는 공변 제네릭 대리자를 선언하는 방법을 보여줍니다.

public delegate R DCovariant<out R>();  

제네릭 대리자에서 in 키워드를 사용하여 제네릭 타입 매개변수를 반변성으로 선언할 수 있습니다. 반공변성 형식은 메서드 반환 형식이 아닌 메서드 인수의 형식으로만 사용할 수 있습니다. 다음 코드 예제에서는 반공변 제네릭 대리자를 선언하는 방법을 보여줍니다.

public delegate void DContravariant<in A>(A a);  

중요합니다

ref, inout C#의 매개 변수를 변형으로 표시할 수 없습니다.

동일한 대리자에서 분산 및 공변성 모두를 지원할 수도 있지만 다른 형식 매개 변수에 대해서도 지원합니다. 이 방법은 다음 예제에서 확인할 수 있습니다.

public delegate R DVariant<in A, out R>(A a);  

Variant 제네릭 대리자 인스턴스화 및 호출

고정 대리자를 인스턴스화하고 호출하는 것처럼 변형 대리자를 인스턴스화하고 호출할 수 있습니다. 다음 예제에서는 대리자가 람다 식으로 인스턴스화됩니다.

DVariant<String, String> dvariant = (String str) => str + " ";  
dvariant("test");  

변형된 제네릭 대리자 결합하기

변형 대리자를 결합하지 마세요. 이 메서드는 Combine 변형 대리자 변환을 지원하지 않으며 대리자가 정확히 동일한 형식이어야 합니다. 다음 코드 예제와 같이 메서드를 사용하거나 연산자를 사용하여 Combine+ 대리자를 결합할 때 런타임 예외가 발생할 수 있습니다.

Action<object> actObj = x => Console.WriteLine("object: {0}", x);  
Action<string> actStr = x => Console.WriteLine("string: {0}", x);  
// All of the following statements throw exceptions at run time.  
// Action<string> actCombine = actStr + actObj;  
// actStr += actObj;  
// Delegate.Combine(actStr, actObj);  

값 및 참조 형식에 대한 제네릭 형식 매개 변수의 분산

제네릭 형식 매개 변수에 대한 분산은 참조 형식에 대해서만 지원됩니다. 예를 들어, 정수는 값 유형이기 때문에 DVariant<int>DVariant<Object>이나 DVariant<long>로 암시적으로 변환할 수 없습니다.

다음 예제에서는 제네릭 형식 매개 변수의 분산이 값 형식에 대해 지원되지 않는 것을 보여 줍니다.

// The type T is covariant.  
public delegate T DVariant<out T>();  
  
// The type T is invariant.  
public delegate T DInvariant<T>();  
  
public static void Test()  
{  
    int i = 0;  
    DInvariant<int> dInt = () => i;  
    DVariant<int> dVariantInt = () => i;  
  
    // All of the following statements generate a compiler error  
    // because type variance in generic parameters is not supported  
    // for value types, even if generic type parameters are declared variant.  
    // DInvariant<Object> dObject = dInt;  
    // DInvariant<long> dLong = dInt;  
    // DVariant<Object> dVariantObject = dVariantInt;  
    // DVariant<long> dVariantLong = dVariantInt;
}  

참고하십시오