Bagikan melalui


Varian dalam Delegat (C#)

.NET Framework 3.5 memperkenalkan dukungan varians untuk mencocokkan tanda tangan metode dengan jenis delegasi di semua delegasi di C#. Ini berarti Anda dapat menetapkan ke delegasi tidak hanya metode yang memiliki tanda tangan yang cocok, tetapi juga metode yang mengembalikan tipe yang lebih turunan (kovariansi) atau yang menerima parameter dengan tipe yang kurang turunan (kontravariansi) dibandingkan yang ditentukan oleh tipe delegasi. Ini termasuk delegasi generik dan non-generik.

Misalnya, pertimbangkan kode berikut, yang memiliki dua kelas dan dua delegasi: generik dan non-generik.

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

Saat membuat delegasi jenis SampleDelegate atau SampleGenericDelegate<A, R> , Anda dapat menetapkan salah satu metode berikut untuk delegasi tersebut.

// 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(); }  

Contoh kode berikut mengilustrasikan konversi implisit antara tanda tangan metode dan jenis delegasi.

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

Untuk contoh selengkapnya, lihat Menggunakan Varians pada Delegasi (C#) dan Menggunakan Varians untuk Delegasi Generik Func dan Action (C#).

Varians dalam Parameter Jenis Generik

Di .NET Framework 4 atau yang lebih baru, Anda dapat memungkinkan konversi implisit antar delegasi, sehingga delegasi generik dengan jenis berbeda yang ditentukan oleh parameter jenis generik dapat saling ditugaskan, asalkan jenis tersebut diwarisi satu sama lain sesuai dengan persyaratan variasi.

Untuk mengaktifkan konversi implisit, Anda harus secara eksplisit mendeklarasikan parameter generik dalam delegasi sebagai kovarian atau kontravarian dengan menggunakan in kata kunci atau out .

Contoh kode berikut menunjukkan bagaimana Anda dapat membuat delegasi yang memiliki parameter jenis generik kovarian.

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

Jika Anda hanya menggunakan dukungan varian untuk mencocokkan tanda tangan metode dengan jenis delegasi dan tidak menggunakan kata kunci in dan out, Anda mungkin menemukan bahwa terkadang Anda dapat membuat instans delegasi dengan ekspresi lambda atau metode yang identik, tetapi Anda tidak dapat menetapkan satu delegasi ke delegasi lainnya.

Dalam contoh kode berikut, SampleGenericDelegate<String> tidak dapat dikonversi secara eksplisit ke SampleGenericDelegate<Object>, meskipun String mewarisi Object. Anda dapat memperbaiki masalah ini dengan menandai parameter T generik dengan out kata kunci.

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

Delegasi Generik yang Memiliki Parameter Jenis Varian di .NET

.NET Framework 4 memperkenalkan dukungan varians untuk parameter jenis generik di beberapa delegasi generik yang ada:

Untuk informasi dan contoh selengkapnya, lihat Using Variance for Func and Action Generic Delegates (C#).

Mendeklarasikan Parameter Tipe Variabel pada Delegasi Generik

Jika delegasi generik memiliki parameter jenis generik kovarian atau kontravarian, maka dapat disebut sebagai delegasi generik varian.

Anda dapat mendeklarasikan kovarian parameter jenis generik dalam delegasi generik dengan menggunakan out kata kunci. Jenis kovarian hanya dapat digunakan sebagai jenis pengembalian metode dan bukan sebagai jenis argumen metode. Contoh kode berikut menunjukkan cara mendeklarasikan delegasi generik kovarian.

public delegate R DCovariant<out R>();  

Anda dapat mendeklarasikan parameter jenis generik kontravarian dalam delegasi generik dengan menggunakan in kata kunci. Jenis kontravarian hanya dapat digunakan sebagai jenis argumen metode dan bukan sebagai jenis pengembalian metode. Contoh kode berikut menunjukkan cara mendeklarasikan delegasi generik kontravarian.

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

Penting

ref, in, dan out parameter di C# tidak dapat ditandai sebagai varian.

Dimungkinkan juga untuk mendukung variansi dan kovarians dalam delegasi yang sama, tetapi untuk parameter jenis yang berbeda. Ini ditunjukkan dalam contoh berikut.

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

Menginstansiasi dan Memanggil Delegasi Generik Varian

Anda dapat menginstansiasi dan memanggil delegate varian sama seperti Anda menginstansiasi dan memanggil delegate yang invarian. Dalam contoh berikut, delegasi diinisialisasi menggunakan ekspresi lambda.

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

Menggabungkan Delegat Generik Variatif

Jangan gabungkan delegasi yang bervariasi. Metode Combine ini tidak mendukung konversi delegasi varian dan mengharapkan delegasi berjenis yang sama persis. Ini dapat menyebabkan pengecualian saat berjalan ketika Anda mengombinasikan delegate dengan menggunakan metode Combine atau operator +, seperti yang ditunjukkan dalam contoh kode berikut.

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

Variasi pada Parameter Tipe Generik untuk Tipe Nilai dan Tipe Referensi

Variasi pada parameter jenis generik hanya didukung untuk tipe referensi. Misalnya, DVariant<int> tidak dapat dikonversi secara implisit ke DVariant<Object> atau DVariant<long>, karena bilangan bulat adalah jenis nilai.

Contoh berikut menunjukkan bahwa varians pada parameter tipe generik tidak didukung untuk tipe nilai.

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

Lihat juga