Ich habe ein Problem mit einer eigenen FxCop Regel. Ich möchte alle Methoden Aufrufe analysieren und dabei werden nicht alle aufgelöst. Speziell die innerhalb eines "yield return" werden nicht aufgelöst. Gegeben ist exemplarisch folgendes:
public interface IInterface
{
void Foo();
}
internal class Interface : IInterface
{
private readonly object m_Obj;
public Interface(object obj)
{
m_Obj = obj;
}
public void Foo()
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
var interfaces = GetInterfaces();
}
private static IEnumerable<IInterface> GetInterfaces()
{
yield return new Interface(Resolve());
}
private static object Resolve()
{
return new object();
}
}
Worum es mir geht ist der Methoden Call von Resolve() innerhalb des yield return. Der IL Code sieht so aus:
.method private hidebysig static class [mscorlib]System.Collections.Generic.IEnumerable`1<class OpCode.IInterface>
GetInterfaces() cil managed
{
// Code size 14 (0xe)
.maxstack 1
.locals init ([0] class OpCode.Program/'<GetInterfaces>d__0' V_0,
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<class OpCode.IInterface> V_1)
IL_0000: ldc.i4.s -2
IL_0002: newobj instance void OpCode.Program/'<GetInterfaces>d__0'::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: stloc.1
IL_000a: br.s IL_000c
IL_000c: ldloc.1
IL_000d: ret
} // end of method Program::GetInterfaces
Im FxCop hab ich schon mehreres Versucht. Es wird für die Rückgabe ein Compile Generiertes Object zurück gegeben. Soweit so klar. Aber ich komme einfach nicht an den MethodCall vom Resolve() ran.
...
Visit(method.Body);
foreach (InstanceInitializer instanceInitializer in method.Instructions().Select(t => t.Value).OfType<InstanceInitializer>())
{
Visit(instanceInitializer.Body);
}
...
Weiß einer wie ich wirklich an ALLE Methodenaufrufe heran komme? Gleiches Problem wird sicherlich auch bei Lamda's auftauchen.
Again what learned...
Hallo rollerfreak2,
GetInterfaces ruft Resolve wirklich nie auf. Resolve würde erst aufgerufen werden, wenn das von GetInterfaces zurückgegebene IEnumerable<> tatsächlich enummeriert werden würde. Das passiert in deinem Code(ausschnitt) aber nirgends. Im Ergebnis wird Resolve an keiner Stelle aufgerufen. Der Compiler könnte theoretisch - solange tatsächlich nie und nirgends enummeriert wird - im Grunde so weit gehen, und die Methode Resolve komplett wegoptimieren.
Es ist hat so, dass bei Compiler Magic die Dinge nicht so sind, wie sie sich im Code darstellen. Damit muss man leben.
herbivore
Hi herbivore,
das hab ich auch erst gedacht. Aber selbst wenn ich im Main das Foo rufe um das Enumerable auszuführen sieht der IL identisch aus.
public static void Main(string[] args)
{
foreach (var itf in GetInterfaces())
{
itf.Foo();
}
}
.method public hidebysig static class [mscorlib]System.Collections.Generic.IEnumerable`1<class OpCode.IInterface>
GetInterfaces() cil managed
{
// Code size 14 (0xe)
.maxstack 1
.locals init ([0] class OpCode.Program/'<GetInterfaces>d__0' V_0,
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<class OpCode.IInterface> V_1)
IL_0000: ldc.i4.s -2
IL_0002: newobj instance void OpCode.Program/'<GetInterfaces>d__0'::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: stloc.1
IL_000a: br.s IL_000c
IL_000c: ldloc.1
IL_000d: ret
} // end of method Program::GetInterfaces
Again what learned...
Hallo rollerfreak2,
wie ich schon schrieb: "GetInterfaces ruft Resolve wirklich nie auf."
Aufgerufen wird Resolve (indirekt) vom foreach im Main.
Das macht eben die Compiler-Magie.
herbivore
Hab das Problem jetzt gefunden. Das Resolve wird halt nicht direkt sondern indirekt gerufen.
Wenn man den nested Type der vom Compiler erzeugt wird mit analysiert, dann findet man in der MoveNext Implementierung den Resolve Aufruf. (IL_0026)
.method private hidebysig newslot virtual final
instance bool MoveNext() cil managed
{
.override [mscorlib]System.Collections.IEnumerator::MoveNext
// Code size 78 (0x4e)
.maxstack 2
.locals init ([0] bool CS$1$0000,
[1] int32 CS$4$0001)
IL_0000: ldarg.0
IL_0001: ldfld int32 OpCode.Program/'<GetInterfaces>d__0'::'<>1__state'
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: switch (
IL_0019,
IL_0017)
IL_0015: br.s IL_001b
IL_0017: br.s IL_0040
IL_0019: br.s IL_001d
IL_001b: br.s IL_0048
IL_001d: ldarg.0
IL_001e: ldc.i4.m1
IL_001f: stfld int32 OpCode.Program/'<GetInterfaces>d__0'::'<>1__state'
IL_0024: nop
IL_0025: ldarg.0
IL_0026: call object OpCode.Program::Resolve()
IL_002b: newobj instance void OpCode.Interface::.ctor(object)
IL_0030: stfld class OpCode.IInterface OpCode.Program/'<GetInterfaces>d__0'::'<>2__current'
IL_0035: ldarg.0
IL_0036: ldc.i4.1
IL_0037: stfld int32 OpCode.Program/'<GetInterfaces>d__0'::'<>1__state'
IL_003c: ldc.i4.1
IL_003d: stloc.0
IL_003e: br.s IL_004c
IL_0040: ldarg.0
IL_0041: ldc.i4.m1
IL_0042: stfld int32 OpCode.Program/'<GetInterfaces>d__0'::'<>1__state'
IL_0047: nop
IL_0048: ldc.i4.0
IL_0049: stloc.0
IL_004a: br.s IL_004c
IL_004c: ldloc.0
IL_004d: ret
} // end of method '<GetInterfaces>d__0'::MoveNext
Again what learned...