Bagikan melalui


Varians dalam Delegasi (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 pada delegasi bukan hanya metode yang memiliki tanda tangan yang cocok, tetapi juga metode yang mengembalikan lebih banyak jenis turunan (kovariansi) atau yang menerima parameter yang memiliki jenis turunan yang lebih sedikit (kontravariansi) daripada yang ditentukan oleh jenis 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 dari 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 dalam Delegasi (C#) dan Menggunakan Varians untuk Delegasi Generik Fungsi dan Tindakan (C#).

Varians dalam Parameter Jenis Generik

Di .NET Framework 4 atau versi lebih baru, Anda dapat mengaktifkan konversi implisit antara delegasi, sehingga delegasi generik yang memiliki jenis berbeda yang ditentukan oleh parameter tipe generik dapat ditetapkan satu sama lain, jika jenis diwarisi dari satu sama lain seperti yang dipersyaratkan oleh varians.

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

Contoh kode berikut menunjukkan bagaimana Anda bisa 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 varians 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 atau metode lambda 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 generik T dengan kata kunci 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;  
  
}  

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 Menggunakan Varians untuk Delegasi Generik Func dan Tindakan (C#).

Mendeklarasikan Parameter Jenis Varian dalam Delegasi Generik

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

Anda dapat mendeklarasikan kovarian parameter jenis generik dalam delegasi generik dengan menggunakan kata kunci out. 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 kontravarian parameter jenis generik dalam delegasi generik dengan menggunakan kata kunci in. 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

Parameter ref, in, dan out dalam C# tidak dapat ditandai sebagai varian.

Juga dimungkinkan untuk mendukung varians dan kovarians dalam delegasi yang sama, tetapi untuk parameter jenis yang berbeda. Hal ini ditunjukkan di contoh berikut.

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

Membuat Instans dan Memanggil Delegasi Generik Varian

Anda dapat membuat instans dan memanggil delegasi varian sama seperti Anda membuat instans dan memanggil delegasi invarian. Dalam contoh berikut, delegasi dibuat oleh ekspresi lambda.

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

Menggabungkan Delegasi Generik Varian

Jangan gabungkan delegasi varian. Metode Combine ini tidak mendukung konversi delegasi varian dan mengharapkan delegasi memiliki jenis yang sama persis. Ini dapat menyebabkan pengecualian runtime saat Anda menggabungkan delegasi baik dengan menggunakan metode Combine atau dengan menggunakan 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);  

Varians dalam Parameter Jenis Generik untuk Jenis Nilai dan Referensi

Varians untuk parameter jenis generik hanya mendukung jenis 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 dalam parameter jenis generik tidak didukung untuk jenis 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