Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
.NET Framework 3.5 introducerade variansstöd för att matcha metodsignaturer med delegattyper i alla delegater i C#. Det innebär att du kan tilldela ombud inte bara metoder som har matchande signaturer, utan även metoder som returnerar fler härledda typer (kovarians) eller som accepterar parametrar som har mindre härledda typer (kontravarians) än de som anges av ombudstypen. Detta omfattar både allmänna och icke-generiska ombud.
Tänk till exempel på följande kod, som har två klasser och två ombud: generiska och icke-generiska.
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
När du skapar ombud av typerna SampleDelegate eller SampleGenericDelegate<A, R> kan du tilldela någon av följande metoder till dessa ombud.
// 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(); }
Följande kodexempel illustrerar den implicita konverteringen mellan metodsignaturen och ombudstypen.
// 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;
Fler exempel finns i Using Variance in Delegates (C#) och Using Variance for Func and Action Generic Delegates (C#).
Varians i parametrar av allmän typ
I .NET Framework 4 eller senare kan du aktivera implicit konvertering mellan ombud, så att allmänna ombud som har olika typer som anges av generiska typparametrar kan tilldelas till varandra, om typerna ärvs från varandra enligt variansens krav.
Om du vill aktivera implicit konvertering måste du uttryckligen deklarera generiska parametrar i en delegat som kovariant eller kontravariant genom att använda nyckelordet in eller out.
Följande kodexempel visar hur du kan skapa en delegering med en kovariant generisk typparameter.
// 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;
}
Om du endast använder avvikelsestöd för att matcha metodsignaturer med ombudstyper och inte använder nyckelorden in och out kanske du upptäcker att du ibland kan instansiera ombud med identiska lambda-uttryck eller metoder, men du kan inte tilldela en delegat till en annan.
I följande kodexempel SampleGenericDelegate<String> kan inte uttryckligen konverteras till SampleGenericDelegate<Object>, men String ärver Object. Du kan åtgärda det här problemet genom att markera den generiska parametern T med nyckelordet 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;
}
Generiska delegater med varianttyp-parametrar i .NET
.NET Framework 4 introducerade variansstöd för generiska typparametrar i flera befintliga generiska ombud:
Actiondelegerade från System namnområdet, till exempel Action<T> och Action<T1,T2>Funcdelegerade från System namnområdet, till exempel Func<TResult> och Func<T,TResult>Ombudet Predicate<T>
Ombudet Comparison<T>
Ombudet Converter<TInput,TOutput>
Mer information och exempel finns i Använda varians för Func och Action generiska delegeringar (C#).
Deklarera varianttypsparametrar i generiska delegeringar
Om en generisk delegering har parametrar av covariant eller kontravariant generisk typ kan den kallas för ett generiskt variantdelegat.
Du kan deklarera en typparameter generisk och kovariant i en generisk delegering med hjälp av nyckelordet out. Den covarianta typen kan endast användas som en metodreturtyp och inte som en typ av metodargument. Följande kodexempel visar hur du deklarerar en kovariant generisk delegering.
public delegate R DCovariant<out R>();
Du kan deklarera en generisk typparameter kontravariant i en generisk delegat med nyckelordet in. Typen contravariant kan endast användas som en typ av metodargument och inte som en metodreturtyp. I följande kodexempel visas hur du deklarerar en kontravariant generisk delegering.
public delegate void DContravariant<in A>(A a);
Viktigt!
ref, inoch out parametrar i C# kan inte markeras som variant.
Det är också möjligt att stödja både varians och kovarians i samma delegering, men för olika typparametrar. Detta visas i följande exempel.
public delegate R DVariant<in A, out R>(A a);
Instansiera och anropa generiska variantdelegater
Du kan instansiera och anropa variantdelegater precis som du instansierar och anropar invarianta ombud. I följande exempel instansieras ombudet av ett lambda-uttryck.
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");
Kombinera generiska variantdelegater
Kombinera inte variantdelegater. Metoden Combine stöder inte variantdelegatkonvertering och förväntar sig att ombuden ska vara av exakt samma typ. Detta kan leda till ett körningsfel när du kombinerar ombud antingen med hjälp av Combine metoden eller med hjälp av operatorn +, som du ser i följande kodexempel.
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 i generiska typparametrar för värde- och referenstyper
Avvikelse för generiska typparametrar stöds endast för referenstyper. Det går till exempel DVariant<int> inte att implicit konvertera till DVariant<Object> eller DVariant<long>, eftersom heltal är en värdetyp.
I följande exempel visas att variansen i generiska typparametrar inte stöds för värdetyper.
// 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;
}