Rozptyl delegátů (C#)
Rozhraní .NET Framework 3.5 zavedlo podporu odchylky pro porovnávání podpisů metod s typy delegátů ve všech delegátech v jazyce C#. To znamená, že delegátům můžete přiřadit nejen metody, které mají odpovídající podpisy, ale také metody, které vracejí více odvozených typů (kovariance) nebo přijímají parametry, které mají méně odvozených typů (kontravariance), než které určuje typ delegáta. To zahrnuje obecné i ne generické delegáty.
Představte si například následující kód, který má dvě třídy a dva delegáty: obecné a ne generické.
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
Při vytváření delegátů nebo SampleDelegate
SampleGenericDelegate<A, R>
typů můžete těmto delegátům přiřadit některou z následujících metod.
// 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(); }
Následující příklad kódu znázorňuje implicitní převod mezi podpisem metody a typem delegáta.
// 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;
Další příklady najdete v tématu Použití odchylek v delegátech (C#) a Použití rozptylu pro obecné delegáty func a akce (C#).
Variance v parametrech obecného typu
V rozhraní .NET Framework 4 nebo novějším můžete povolit implicitní převod mezi delegáty, takže obecné delegáty, které mají různé typy určené parametry obecného typu, se dají přiřadit k sobě navzájem, pokud jsou typy zděděny od sebe podle potřeby rozptylem.
Chcete-li povolit implicitní převod, musíte explicitně deklarovat obecné parametry v delegátu jako kovariantní nebo kontravariant pomocí klíčového slova nebo out
klíčového in
slova.
Následující příklad kódu ukazuje, jak můžete vytvořit delegáta, který má kovariantní obecný typ parametr.
// 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;
}
Pokud používáte pouze podporu odchylek pro porovnávání podpisů metod s typy delegátů a nepoužíváte in
tato out
klíčová slova, můžete zjistit, že někdy můžete vytvořit instanci delegátů s identickými výrazy lambda nebo metodami, ale nemůžete přiřadit jednoho delegáta k druhému.
V následujícím příkladu SampleGenericDelegate<String>
kódu nelze explicitně převést na SampleGenericDelegate<Object>
, ačkoli String
dědí Object
. Tento problém můžete vyřešit tím, že označíte obecný parametr T
klíčovým slovem 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;
}
Obecné delegáty, kteří mají parametry variantního typu v .NET
Rozhraní .NET Framework 4 zavedlo podporu odchylek pro parametry obecného typu v několika existujících obecných delegátech:
Action
delegáty z System oboru názvů, například Action<T> a Action<T1,T2>Func
delegáty z System oboru názvů, například Func<TResult> a Func<T,TResult>Delegát Predicate<T>
Delegát Comparison<T>
Delegát Converter<TInput,TOutput>
Další informace a příklady najdete v tématu Použití rozptylu pro obecné delegáty Func a Action (C#).
Deklarování parametrů variantního typu v obecných delegátech
Pokud má obecný delegát kovariantní nebo kontravariantní parametry obecného typu, může být označován jako variantní obecný delegát.
Pomocí klíčového slova můžete deklarovat kovariantní parametr obecného typu v obecném delegátu out
. Kovariantní typ lze použít pouze jako návratový typ metody, nikoli jako typ argumentů metody. Následující příklad kódu ukazuje, jak deklarovat kovariantní obecný delegát.
public delegate R DCovariant<out R>();
Parametr obecného typu můžete deklarovat kontravariant v obecném delegátu pomocí klíčového in
slova. Kontravariantní typ lze použít pouze jako typ argumentů metody, nikoli jako návratový typ metody. Následující příklad kódu ukazuje, jak deklarovat kontravariant obecný delegát.
public delegate void DContravariant<in A>(A a);
Důležité
ref
, in
a out
parametry v jazyce C# nelze označit jako variantu.
Je také možné podporovat odchylku i kovarianci ve stejném delegátu, ale pro různé parametry typu. To je ukázáno v následujícím příkladu.
public delegate R DVariant<in A, out R>(A a);
Vytvoření instance a vyvolání obecných delegátů variant
Instance a vyvolání delegátů variant můžete vyvolat stejně jako vytvoření instance a vyvolání invariantní delegáty. V následujícím příkladu vytvoří delegát instanci výrazu lambda.
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");
Kombinování obecných delegátů variant
Nekombinujte variantní delegáty. Metoda Combine nepodporuje převod variantního delegáta a očekává, že delegáti budou přesně stejného typu. To může vést k výjimce za běhu při kombinování delegátů buď pomocí Combine metody, nebo pomocí operátoru +
, jak je znázorněno v následujícím příkladu kódu.
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);
Rozptyl v parametrech obecného typu pro hodnoty a odkazové typy
Variance parametrů obecného typu je podporována pouze pro odkazové typy. DVariant<int>
Například nelze implicitně převést na DVariant<Object>
nebo DVariant<long>
, protože celé číslo je typ hodnoty.
Následující příklad ukazuje, že odchylka v parametrech obecného typu není podporována pro typy hodnot.
// 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;
}