brainbaking/content/wiki/code/csharp/reflectie.md

269 lines
9.1 KiB
Markdown
Raw Normal View History

2017-02-26 13:28:18 +01:00
+++
title = "reflectie"
draft = false
tags = [
"code",
"csharp",
"reflectie"
]
date = "2015-02-09"
+++
# Reflectie
## Get Type based on string
Probleem: een fully qualified classname in string vorm is geen `Type` en `Type.GetType()` gaat standaard in de huidige assembly kijken of die klasse daar in steekt. Hoe haal ik dit type op als ik niet vanbuiten weet in welke assembly ik moet gaan kijken? Via uw `AppDomain`:
```csharp
return AppDomain.CurrentDomain.GetAssemblies()
.Single(app ######> app.GetType(fullClassName) ! null)
.GetType(fullClassName);
```
## Get Subclasses/interfaces of class
Gebruik `type.Assembly.GetTypes()` en filter verder.
1. Als je een **interface** wil hebben, moet je `IsAssignableFrom()` gebruiken (in de omgekeerde richting).
2. Als je een **subklasse** wil hebben, moet je ofwel de `.BaseType` property gebruiken als direct kind, ofwel de `IsSubclassOf()` method.
```csharp
public string HandleMessage(IHostService hostService)
{
var requestType = typeof (IHostServiceNativeRequest);
var handlerType = requestType.Assembly.GetTypes()
.Where(requestType.IsAssignableFrom)
.Single(t ######> (NativeRequestInstance(t)).Key Key);
return NativeRequestInstance(handlerType).ParseRequest(hostService);
}
private IHostServiceNativeRequest NativeRequestInstance(Type t)
{
return (IHostServiceNativeRequest) Activator.CreateInstance(t);
}
```
## Reflective instantiation
Zie vorig voorbeeld; een nieuwe instantie van een `Type` maken kan via `Activator.CreateInstance()`.
##### Protected constructor with arguments
```csharp
return (T)Activator.CreateInstance(typeof(T),
BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance,
null, new object[] { arg1 }, CultureInfo.CurrentCulture);
```
## AppDomains
##### Q: Ik wil een DLL dynamisch laden
Gebruik `Assembly.LoadFrom(string)`.
##### Q: Ik wil loopen over alle AppDomains
Niet zo simpel te realiseren: (zie [http:*stackoverflow.com/questions/14758915/get-all-processes-with-their-corresponding-app-domains](http:*stackoverflow.com/questions/14758915/get-all-processes-with-their-corresponding-app-domains))
```csharp
private static List<AppDomainInf> GetAppDomains()
{
IList<AppDomain> mAppDomainsList = new List<AppDomain>();
List<AppDomainInf> mAppDomainInfos = new List<AppDomainInf>();
IntPtr menumHandle = IntPtr.Zero;
ICorRuntimeHost host = new CorRuntimeHost();
try
{
host.EnumDomains(out menumHandle);
object mTempDomain = null;
//add all the current app domains running
while (true)
{
host.NextDomain(menumHandle, out mTempDomain);
if (mTempDomain ###### null) break;
AppDomain tempDomain = mTempDomain as AppDomain;
mAppDomainsList.Add((tempDomain));
}
//retrieve every app domains detailed information
foreach (var appDomain in mAppDomainsList)
{
AppDomainInf domainInf = new AppDomainInf();
domainInf.Assemblies = GetAppDomainAssemblies(appDomain);
domainInf.AppDomainName = appDomain.FriendlyName;
mAppDomainInfos.Add(domainInf);
}
return mAppDomainInfos;
}
catch (Exception)
{
throw; //rethrow
}
finally
{
host.CloseEnum(menumHandle);
Marshal.ReleaseComObject(host);
}
}
```
Ref toevoegen, mscoree.tld in .NET root folder.
############= Generic Type arguments & reflectie ############=
###### Q: Ik wil een Type meegeven dat moet extenden van een basisklasse, waarna ik dat type wil instantiëren. ######
```csharp
protected IList<TInsertable> Load<TInsertable>() where TInsertable : DatabaseInsertable
{
var myInstance = Activator.CreateInstance(typeof(TInsertable));
}
```
:exclamation: `Type` zelf is niet generic omdat dit at-runtime gebruikt wordt voor typeinformatie en de andere dingen at-compiletime. Je kan dus geen `Type<T> where T : MyClass` gebruiken, zoals in Java bijvoorbeeld `Class<? extends MyClass>` gebruikt wordt. Merk op dat in Java er met het generic type argument geen klasse aangemaakt kan worden, zie [code/java/reflectie]({{< relref "wiki/code/java/reflectie.md" >}}) voor java.
###### Q: Ik wil een variabel aantal generic type argumenten definiëren ######
Genaaid, dit gaat niet. Kijk maar naar bijvoorbeeld `Func<>`:
1. `Func<in T1, in T2, out Result>`
2. `Func<in T1, in T2, in T3, out Result>`
3. `Func<in T1, in T2, in T3, in T4, out Result>`
4. ...
Je kan die wel allemaal laten refereren naar één (private) methode die `params[]` gebruikt, bijvoorbeeld onderstaande count:
```csharp
protected int Count<TInsertable>()
where TInsertable : DatabaseInsertable
{
return Count(typeof (TInsertable));
}
protected int Count<TInsertable1, TInsertable2>()
where TInsertable1 : DatabaseInsertable
where TInsertable2 : DatabaseInsertable
{
return Count(typeof (TInsertable1), typeof (TInsertable2));
}
protected int Count<TInsertable1, TInsertable2, TInsertable3>()
where TInsertable1 : DatabaseInsertable
where TInsertable2 : DatabaseInsertable
where TInsertable3 : DatabaseInsertable
{
return Count(typeof (TInsertable1), typeof (TInsertable2), typeof (TInsertable3));
}
private int Count(params Type[] insertableTypes)
{
var count = 0;
foreach (var type in insertableTypes)
{
var select = "select count(*) from " + GetTableOfType(type);
count += Int32.Parse(factory.CreateCommand(this.connection, select).ExecuteScalar().ToString());
}
return count;
}
```
############= Reflectie en dynamisch code genereren ############=
Mogelijk met **Reflection EMIT**, om dynamisch IL code te genereren. IL is de bytecode tussenlaag in .NET, die je ook in C# kan schrijven.
Compleet voorbeeld: http://www.codeproject.com/Articles/121568/Dynamic-Type-Using-Reflection-Emit
Bijvoorbeeld, om een getal te delen door een ander met `getal / other`, genereert de volgende code dit in IL:
```csharp
MethodBuilder mDivide = tbuilder.DefineMethod("Divide", MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.NewSlot |
MethodAttributes.Virtual |
MethodAttributes.Final,
CallingConventions.Standard,
typeof(System.Single),
new Type[] { typeof(System.Int32), typeof(System.Int32) });
mDivide.SetImplementationFlags(MethodImplAttributes.Managed);
ILGenerator dil = mDivide.GetILGenerator();
dil.Emit(OpCodes.Nop);
Label lblTry = dil.BeginExceptionBlock();
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldarg_1);
dil.Emit(OpCodes.Ldarg_2);
dil.Emit(OpCodes.Div);
dil.Emit(OpCodes.Conv_R4); // Converts to Float32
dil.Emit(OpCodes.Stloc_1);
dil.Emit(OpCodes.Leave, lblTry);
dil.BeginCatchBlock(typeof(DivideByZeroException));
dil.Emit(OpCodes.Stloc_0);
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldstr, "ZeroDivide exception : {0}");
dil.Emit(OpCodes.Ldloc_0);
MethodInfo minfo = typeof(DivideByZeroException).GetMethod("get_Message");
dil.Emit(OpCodes.Callvirt, minfo);
MethodInfo wl = typeof(System.Console).GetMethod("WriteLine", new Type[]
{ typeof(string), typeof(object) });
dil.Emit(OpCodes.Call, wl);
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldc_R4, 0.0);
dil.Emit(OpCodes.Stloc_1);
dil.Emit(OpCodes.Leave_S, lblTry);
dil.EndExceptionBlock();
dil.Emit(OpCodes.Nop);
dil.Emit(OpCodes.Ldloc_1);
dil.Emit(OpCodes.Ret);
```
Genereert dit in IL:
```
.method public hidebysig newslot virtual final
instance float32 Divide(int32 firstnum,
int32 secondnum) cil managed
{
// Code size 39 (0x27)
.maxstack 2
.locals init (class [mscorlib]System.DivideByZeroException V_0,
float32 V_1)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldarg.1
IL_0003: ldarg.2
IL_0004: div
IL_0005: conv.r4
IL_0006: stloc.1
IL_0007: leave.s IL_0024
} // end .try
catch [mscorlib]System.DivideByZeroException
{
IL_0009: stloc.0
IL_000a: nop
IL_000b: ldstr "ZeroDivide exception : {0}"
IL_0010: ldloc.0
IL_0011: callvirt instance string [mscorlib]System.Exception::get_Message()
IL_0016: call void [mscorlib]System.Console::WriteLine(string,
object)
IL_001b: nop
IL_001c: ldc.r4 0.0
IL_0021: stloc.1
IL_0022: leave.s IL_0024
} // end handler
IL_0024: nop
IL_0025: ldloc.1
IL_0026: ret
}
```