대리자의 가변성(C# 및 Visual Basic)

.NET Framework 3.5 및 Visual Studio 2008에는 메서드 시그니처를 C# 및 Visual Basic에 있는 모든 대리자의 대리자 형식과 일치시키는 가변성(variance) 지원이 추가되었습니다. 즉, 일치하는 시그니처가 있는 메서드뿐 아니라 대리자 형식에 지정된 것보다 더 많은 파생 형식을 반환하거나(공변성) 더 적은 파생 형식을 받아들이는(반공변성) 메서드도 대리자에 할당할 수 있습니다. 여기에는 제네릭 대리자와 제네릭이 아닌 대리자가 모두 포함됩니다.

예를 들어, 클래스와 대리자가 제네릭과 제네릭이 아닌 것으로 각각 두 개씩 있는 다음 코드를 생각해 볼 수 있습니다.

Public Class First
End Class

Public Class Second
    Inherits First
End Class

Public Delegate Function SampleDelegate(ByVal a As Second) As First
Public Delegate Function SampleGenericDelegate(Of A, R)(ByVal a As A) As R
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>(Visual Basic의 경우 SampleDelegate(Of A, R)) 형식의 대리자를 만드는 경우 이러한 대리자에 다음 메서드 중 하나를 할당할 수 있습니다.

' Matching signature.
Public Shared Function ASecondRFirst(
    ByVal second As Second) As First
    Return New First()
End Function

' The return type is more derived.
Public Shared Function ASecondRSecond(
    ByVal second As Second) As Second
    Return New Second()
End Function

' The argument type is less derived.
Public Shared Function AFirstRFirst(
    ByVal first As First) As First
    Return New First()
End Function

' The return type is more derived 
' and the argument type is less derived.
Public Shared Function AFirstRSecond(
    ByVal first As First) As Second
    Return New Second()
End Function
// Matching signature.
public static First ASecondRFirst(Second first)
{ 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.
Dim dNonGeneric As SampleDelegate = AddressOf 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.
Dim dNonGenericConversion As SampleDelegate = AddressOf AFirstRSecond

' Assigning a method with a matching signature to a generic delegate.
' No conversion is necessary.
Dim dGeneric As SampleGenericDelegate(Of Second, First) = AddressOf ASecondRFirst
' Assigning a method with a more derived return type 
' and less derived argument type to a generic delegate.
' The implicit conversion is used.
Dim dGenericConversion As SampleGenericDelegate(Of Second, First) = AddressOf AFirstRSecond
// 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# 및 Visual Basic)Func 및 Action 제네릭 대리자에 가변성 사용(C# 및 Visual Basic)을 참조하십시오.

제네릭 형식 매개 변수의 가변성(variance)

.NET Framework 4에서는 대리자 간의 암시적 변환이 가능합니다. 이는 대리자가 가변성(variance)에 필요한 형식을 서로 상속하는 경우 제네릭 형식 매개 변수에 지정된 다양한 형식을 가진 제네릭 대리자를 서로 할당할 수 있도록 하기 위해서입니다.

암시적 변환이 가능하게 하려면 in 또는 out 키워드를 사용하여 대리자의 제네릭 매개 변수를 공변(covariant) 또는 반공변(contravariant)으로 명시적으로 선언해야 합니다.

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

' Type T is declared covariant by using the out keyword.
Public Delegate Function SampleGenericDelegate(Of Out T)() As T
Sub Test()
    Dim dString As SampleGenericDelegate(Of String) = Function() " "
    ' You can assign delegates to each other,
    ' because the type T is declared covariant.
    Dim dObject As SampleGenericDelegate(Of Object) = dString
End Sub
// 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;           
}

가변성(variance) 지원만 사용하여 메서드 시그니처를 대리자와 일치시킬 때 in 및 out 키워드를 사용하지 않으면 동일한 람다 식 또는 메서드를 사용하여 대리자를 인스턴스화할 수 있지만 대리자를 서로 할당하지 못할 수도 있습니다.

다음 코드 예제에서는 String이 Object를 상속하지만 SampleGenericDelegate<String>(Visual Basic의 경우 SampleGenericDelegate(Of String))를 SampleGenericDelegate<Object>(Visual Basic의 경우 SampleGenericDelegate(Of Object))로 명시적으로 변환할 수 없습니다. out 키워드로 제네릭 매개 변수 T를 표시하면 이 문제를 해결할 수 있습니다.

Public Delegate Function SampleGenericDelegate(Of T)() As T
Sub Test()
    Dim dString As SampleGenericDelegate(Of String) = Function() " "

    ' 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.
    Dim dObject As SampleGenericDelegate(Of Object) = Function() " "

    ' The following statement generates a compiler error
    ' because the generic type T is not marked as covariant.
    ' Dim dObject As SampleGenericDelegate(Of Object) = dString


End Sub
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 Framework에서 가변성(variance) 형식 매개 변수를 가진 제네릭 대리자

.NET Framework 4에는 다음과 같은 여러 기존 제네릭 대리자의 제네릭 형식 매개 변수에 대한 가변성(variance) 지원이 추가되었습니다.

자세한 내용과 예제를 보려면 Func 및 Action 제네릭 대리자에 가변성 사용(C# 및 Visual Basic)을 참조하십시오.

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

제네릭 대리자에 공변(covariant) 또는 반공변(contravariant) 제네릭 형식 매개 변수가 있는 경우 이러한 제네릭 대리자를 variant 제네릭 대리자라고 합니다.

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

Public Delegate Function DCovariant(Of Out R)() As R
public delegate R DCovariant<out R>();

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

Public Delegate Sub DContravariant(Of In A)(ByVal a As A)
public delegate void DContravariant<in A>(A a);

중요

Visual Basic의 ByRef 매개 변수와 C#의 ref 및 out 매개 변수는 variant로 표시할 수 없습니다.

또한 형식 매개 변수만 다른 동일한 대리자에서 가변성(variance) 및 공 분산을 둘 다 지원할 수도 있습니다. 다음 예제에서 이를 확인할 수 있습니다.

Public Delegate Function DVariant(Of In A, Out R)(ByVal a As A) As R
public delegate R DVariant<in A, out R>(A a);

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

invariant 대리자를 인스턴스화하고 호출하는 경우와 마찬가지로 variant 대리자를 인스턴스화하고 호출할 수 있습니다. 다음 예제에서는 대리자가 람다 식으로 인스턴스화됩니다.

Dim dvariant As DVariant(Of String, String) = Function(str) str + " "
dvariant("test")
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");

variant 제네릭 대리자 결합

variant 대리자는 결합하면 안 됩니다. Combine 메서드는 variant 대리자 변환을 지원하지 않으며 대리자를 정확하게 같은 형식으로 간주합니다. 그러면 다음 코드 예제와 같이 Combine 메서드(C# 및 Visual Basic) 또는 + 연산자(C#)를 사용하여 대리자를 결합하는 경우 런타임 예외가 발생할 수 있습니다.

Dim actObj As Action(Of Object) = Sub(x) Console.WriteLine("object: {0}", x)
Dim actStr As Action(Of String) = Sub(x) Console.WriteLine("string: {0}", x)

' The following statement throws an exception at run time.
' Dim actCombine = [Delegate].Combine(actStr, actObj)
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);

값 및 참조 형식에 대한 제네릭 형식 매개 변수의 가변성(variance)

제네릭 형식 매개 변수에 대한 가변성(variance)은 참조 형식에 대해서만 지원됩니다. 예를 들어, 정수가 값 형식이기 때문에 DVariant<int>(Visual Basic의 경우 DVariant(Of Int))를 DVariant<Object> 또는 DVaraint<long>(Visual Basic의 경우 DVariant(Of Object) 또는 DVaraint(Of Long))로 암시적으로 변환할 수 없습니다.

다음 예제에서는 제네릭 형식 매개 변수의 가변성(variance)이 값 형식에 대해 지원되지 않음을 보여 줍니다.

' The type T is covariant.
Public Delegate Function DVariant(Of Out T)() As T
' The type T is invariant.
Public Delegate Function DInvariant(Of T)() As T
Sub Test()
    Dim i As Integer = 0
    Dim dInt As DInvariant(Of Integer) = Function() i
    Dim dVaraintInt As DVariant(Of Integer) = Function() 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.
    ' Dim dObject As DInvariant(Of Object) = dInt
    ' Dim dLong As DInvariant(Of Long) = dInt
    ' Dim dVaraintObject As DInvariant(Of Object) = dInt
    ' Dim dVaraintLong As DInvariant(Of Long) = dInt
End Sub
// 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;            
}

Visual Basic의 완화된 대리자 변환

Visual Basic 2008에 추가된 완화된 대리자 변환은 메서드 시그니처를 대리자 형식과 일치시킬 때 더 많은 융통성을 제공합니다. 예를 들어, 대리자에 메서드를 할당할 때 매개 변수 사양과 함수 반환 값을 생략할 수 있습니다. 자세한 내용은 완화된 대리자 변환(Visual Basic)을 참조하십시오.

참고 항목

작업

방법: 대리자 조합(멀티캐스트 대리자)(C# 프로그래밍 가이드)

참조

Func 및 Action 제네릭 대리자에 가변성 사용(C# 및 Visual Basic)

개념

.NET Framework의 제네릭