Hier ist ziemlich einfach generische Klasse. Der generische Parameter ist auf den Referenztyp beschränkt. IRepository
und DbSet
enthalten ebenfalls dieselbe Einschränkung.Warum wird die Box-Anweisung für generische ausgegeben?
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
protected readonly DbSet<TEntity> _dbSet;
public void Insert(TEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity", "Cannot add null entity.");
_dbSet.Add(entity);
}
}
Kompilierte IL enthält box
Anweisung. Hier ist die Release-Version (die Debug-Version enthält sie aber auch).
.method public hidebysig newslot virtual final
instance void Insert(!TEntity entity) cil managed
{
// Code size 38 (0x26)
.maxstack 8
IL_0000: ldarg.1
>>>IL_0001: box !TEntity
IL_0006: brtrue.s IL_0018
IL_0008: ldstr "entity"
IL_000d: ldstr "Cannot add null entity."
IL_0012: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string,
string)
IL_0017: throw
IL_0018: ldarg.0
IL_0019: ldfld class [EntityFramework]System.Data.Entity.DbSet`1<!0> class Repository`1<!TEntity>::_dbSet
IL_001e: ldarg.1
IL_001f: callvirt instance !0 class [EntityFramework]System.Data.Entity.DbSet`1<!TEntity>::Add(!0)
IL_0024: pop
IL_0025: ret
} // end of method Repository`1::Insert
UPDATE:
Mit object.Equals(entity, default(TEntity))
sieht es noch schlimmer:
.maxstack 2
.locals init ([0] !TEntity CS$0$0000)
IL_0000: ldarg.1
>>>IL_0001: box !TEntity
IL_0006: ldloca.s CS$0$0000
IL_0008: initobj !TEntity
IL_000e: ldloc.0
>>>IL_000f: box !TEntity
IL_0014: call bool [mscorlib]System.Object::Equals(object,
object)
IL_0019: brfalse.s IL_002b
UPDATE2:
Für diejenigen, die interessiert sind, hier ist der Code von JIT kompiliert im Debugger angezeigt:
0cd5af28 55 push ebp
0cd5af29 8bec mov ebp,esp
0cd5af2b 83ec18 sub esp,18h
0cd5af2e 33c0 xor eax,eax
0cd5af30 8945f0 mov dword ptr [ebp-10h],eax
0cd5af33 8945ec mov dword ptr [ebp-14h],eax
0cd5af36 8945e8 mov dword ptr [ebp-18h],eax
0cd5af39 894df8 mov dword ptr [ebp-8],ecx
//entity reference to [ebp-0Ch]
0cd5af3c 8955f4 mov dword ptr [ebp-0Ch],edx
//some debugger checks
0cd5af3f 833d9424760300 cmp dword ptr ds:[3762494h],0
0cd5af46 7405 je 0cd5af4d Branch
0cd5af48 e8e1cac25a call clr!JIT_DbgIsJustMyCode (67987a2e)
0cd5af4d c745fc00000000 mov dword ptr [ebp-4],0
0cd5af54 90 nop
//comparison or entity ref with zero
0cd5af55 837df400 cmp dword ptr [ebp-0Ch],0
0cd5af59 0f95c0 setne al
0cd5af5c 0fb6c0 movzx eax,al
0cd5af5f 8945fc mov dword ptr [ebp-4],eax
0cd5af62 837dfc00 cmp dword ptr [ebp-4],0
//if not zero, jump further
0cd5af66 7542 jne 0cd5afaa Branch
//throwing exception here
Der Grund für diese Frage ist eigentlich, dass NDepend über Boxen/Unboxing warnt. Ich war neugierig, warum es Boxen in einigen generischen Klassen gefunden hat, und jetzt ist es klar.
Ist es dasselbe, wenn Sie object.Equals (Entität, Standard (TEntity)) verwenden? –
Ich werde hier einen Stich in die Dunkelheit machen. IIRC, Interfaces _kann Boxen von Werttypen verursachen, also frage ich mich, ob das etwas damit zu tun hat. Vielleicht denkt der Compiler, dass er es einkisten muss, falls ein Wert-Typ eingegeben wird, um es gegen die 'Null'-Referenz zu prüfen. Ich könnte jedoch voll davon sein. :) EDIT: Dies ist trotz der Einschränkung, dass es eine 'Klasse' (kein Werttyp) ist, aber ich bin nicht sicher, ob das vom Compiler und/oder CLR und/oder IL berücksichtigt wird. –
Ich würde auch vermuten, dass sowohl "object.Equals" als auch "==" operator so _general_ ist, dass es standardmäßig boxt. Versuchen Sie 'EqualityComparer .Default.Equals (Entität, Standard (T))' wie es generische Methode verwenden sollte. –