Formazione
Modulo
Scegliere il tipo di dati corretto nel codice C# - Training
Scegli il tipo di dati corretto per il codice da diversi tipi di base usati in C#.
Questo browser non è più supportato.
Esegui l'aggiornamento a Microsoft Edge per sfruttare i vantaggi di funzionalità più recenti, aggiornamenti della sicurezza e supporto tecnico.
In .NET framework 3.5 è stato introdotto il supporto della varianza per la corrispondenza delle firme del metodo con i tipi di delegati in tutti i delegati in C#. Ciò significa che è possibile assegnare ai delegati non solo i metodi con firme corrispondenti, ma anche i metodi che restituiscono più tipi derivati (covarianza) o accettano parametri con meno tipi derivati (controvarianza) rispetto a quelli specificati dal tipo di delegato. Sono inclusi sia i delegati generici che quelli non generici.
Ad esempio, si consideri il codice seguente, che ha due classi e due delegati: generico e non generico.
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
Quando si creano i delegati dei tipi SampleDelegate
o SampleGenericDelegate<A, R>
è possibile assegnare uno qualsiasi dei metodi seguenti ai delegati.
// 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(); }
L'esempio di codice seguente viene illustra la conversione implicita tra la firma del metodo e il tipo di delegato.
// 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;
Per altri esempi, vedere Uso della varianza nei delegati (C#) e Uso della varianza per i delegati generici Func e Action (C#).
In .NET Framework 4 o versioni successive è possibile abilitare la conversione implicita tra delegati, in modo che i delegati generici con tipi diversi specificati dai parametri di tipo generico possano essere assegnati l'uno all'altro, se i tipi vengono ereditati reciprocamente come richiesto dalla varianza.
Per abilitare la conversione implicita, è necessario dichiarare esplicitamente i parametri generici in un delegato come covariante o controvariante usando la parola chiave in
o out
.
L'esempio di codice seguente illustra come creare un delegato con un parametro di tipo generico covariante.
// 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;
}
Se si usa solo il supporto della varianza per la corrispondenza delle firme del metodo con i tipi delegati e non si usano le parole chiave in
e out
, è possibile che in alcuni casi si possano creare istanze di delegati con metodi o espressioni lambda identici, ma non è possibile assegnare un delegato a un altro.
Nell'esempio di codice seguente non è possibile convertire in modo esplicito SampleGenericDelegate<String>
in SampleGenericDelegate<Object>
, sebbene String
erediti Object
. È possibile correggere questo problema contrassegnando il parametro generico T
con la parola chiave 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;
}
In .NET framework 4 è stato introdotto il supporto della varianza per i parametri di tipo generico in diversi delegati generici esistenti:
Delegati Action
dallo spazio dei nomi System, ad esempio Action<T> e Action<T1,T2>
Delegati Func
dallo spazio dei nomi System, ad esempio Func<TResult> e Func<T,TResult>
Delegato Predicate<T>.
Delegato Comparison<T>.
Delegato Converter<TInput,TOutput>.
Per altre informazioni ed esempi, vedere Uso della varianza per i delegati generici Func e Action (C#).
Se un delegato generico ha parametri di tipo generico covariante o controvariante, può essere indicato come un delegato generico variante.
È possibile dichiarare un parametro di tipo generico covariante in un delegato generico usando la parola chiave out
. Il tipo covariante può essere usato solo come tipo restituito del metodo e non come tipo di argomenti del metodo. Nell'esempio di codice seguente viene illustrato come dichiarare un delegato generico covariante.
public delegate R DCovariant<out R>();
È possibile dichiarare un parametro di tipo generico controvariante in un delegato generico usando la parola chiave in
. Il tipo controvariante può essere usato solo come tipo di argomenti del metodo e non come tipo restituito del metodo. Nell'esempio di codice seguente viene illustrato come dichiarare un delegato generico controvariante.
public delegate void DContravariant<in A>(A a);
Importante
I parametri ref
, in
e out
in C# non possono essere contrassegnati come varianti.
È anche possibile supportare sia la varianza che la covarianza nello stesso delegato, ma per parametri di tipo diverso. come illustrato nell'esempio seguente.
public delegate R DVariant<in A, out R>(A a);
È possibile creare un'istanza e richiamare delegati varianti con la stessa procedura con cui si crea un'istanza e si richiamano i delegati invariabili. Nell'esempio seguente viene creata un'istanza del delegato da un'espressione lambda.
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");
Non combinare delegati varianti. Il metodo Combine non supporta la conversione dei delegati varianti e prevede che i delegati siano esattamente dello stesso tipo. Questo può causare un'eccezione in fase di esecuzione quando si combinano delegati usando il metodo Combine o l'operatore +
, come illustrato nell'esempio di codice seguente.
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);
La varianza per i parametri di tipo generico è supportata solo per i tipi di riferimento. Ad esempio, DVariant<int>
non può essere convertito implicitamente in DVariant<Object>
o DVariant<long>
, poiché integer è un tipo di valore.
L'esempio seguente dimostra che la varianza nei parametri di tipo generico non è supportata per i tipi di valore.
// 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;
}
Feedback su .NET
.NET è un progetto di open source. Selezionare un collegamento per fornire feedback:
Formazione
Modulo
Scegliere il tipo di dati corretto nel codice C# - Training
Scegli il tipo di dati corretto per il codice da diversi tipi di base usati in C#.