Come evitare la boxe / unboxing quando si estende System.Object?
-
25-09-2019 - |
Domanda
Sto lavorando a un metodo di estensione che è applicabile solo ai tipi di riferimento. Penso, tuttavia, è attualmente boxing e unboxing il valore. Come posso evitare questo?
namespace System
{
public static class SystemExtensions
{
public static TResult GetOrDefaultIfNull<T, TResult>(this T obj, Func<T, TResult> getValue, TResult defaultValue)
{
if (obj == null)
return defaultValue;
return getValue(obj);
}
}
}
Esempio di utilizzo:
public class Foo
{
public int Bar { get; set; }
}
In un metodo:
Foo aFooObject = new Foo { Bar = 1 };
Foo nullReference = null;
Console.WriteLine(aFooObject.GetOrDefaultIfNull((o) => o.Bar, 0)); // results: 1
Console.WriteLine(nullReference.GetOrDefaultIfNull((o) => o.Bar, 0)); // results: 0
Soluzione
Questo non è la boxe. Dove pensi che è di pugilato? Se è perché hai guardato il IL intorno "==", non lasciatevi ingannare - il JIT arriva a decidere cosa fare qui. Ha la possibilità di generare il codice nativo diverso per ogni (T
, TResult
) coppia. Infatti, il codice sarà condiviso per tutti i tipi di riferimento, e si differenziano per tipi di valori. Così ci si finisce con:
T = string, TResult = int (native code #1)
T = Stream, TResult = byte (native code #2)
T = string, TResult = byte (native code #2)
T = Stream, TResult = string (native code #3)
Detto questo, se si vuole restringere la metodo di estensione per i tipi di riferimento, fare così:
public static TResult GetOrDefaultIfNull<T, TResult>
(this T obj, Func<T, TResult> getValue, TResult defaultValue)
where T : class
Non ci sarà ancora una scatola nel IL, ma non preoccupatevi - non boxe sarà effettivamente manifestarsi. Dopo tutto, che potrebbero essere inscatolato? Si sta fornendo un punto di riferimento, e riferimenti stessi sono mai in scatola -. Solo valori di tipo valore sono boxed
Altri suggerimenti
Semplicemente, non c'è nulla in quel codice che richiederebbe la boxe. Ci sono scenari dove boxe è inevitabile, e ci sono anche codici operativi supplementari per colmare il divario tra valore / tipi ref ( constrained
) in alcuni casi.
Ma non in questo caso; senza reale Pugilato richiesto (JIT può rimuovere alcuni scatolata casi - ma non tutti, purtroppo)