public interface IMixinAlpha { }
public interface IMixinBeta { }
public static class ComponentExtensions {
#region MI Impl
private static readonly ConditionalWeakTable<IMixinAlpha, MixinAlphaProperties> alphaEphemerons = new ConditionalWeakTable<IMixinAlpha, MixinAlphaProperties>();
private static readonly ConditionalWeakTable<IMixinBeta, MixinBetaProperties> betaEphemerons = new ConditionalWeakTable<IMixinBeta, MixinBetaProperties>();
private static readonly Dictionary<string, Dictionary<Type, MethodInfo>> vtable = new Dictionary<string, Dictionary<Type, MethodInfo>>();
internal static void PopulateVirtualTable() {
var allLoadedTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(ass => ass.GetReferencedAssemblies().Select(Assembly.Load).Concat(new[] { ass }))
.Distinct()
.SelectMany(ass => ass.GetTypes());
var allComponentExtensions = typeof(ComponentExtensions).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.Where(mi => mi.GetCustomAttributes(typeof(ExtensionAttribute), false).Length > 0);
foreach (Type t in allLoadedTypes) {
foreach (MethodInfo extMethod in allComponentExtensions) {
string callerMemberName = extMethod.Name;
var matchingOverride = t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(mi => mi.Name == callerMemberName);
if (matchingOverride != null) {
if (!vtable.ContainsKey(callerMemberName)) vtable[callerMemberName] = new Dictionary<Type, MethodInfo>();
vtable[callerMemberName][t] = matchingOverride;
}
}
}
}
private static bool HasOverride(object targetObj, [CallerMemberName] string callerName = null) {
if (!vtable.ContainsKey(callerName)) return false;
var overridesForMember = vtable[callerName];
var targetType = targetObj.GetType();
return overridesForMember.ContainsKey(targetType);
}
private static T InvokeOverride<T>(object targetObj, object[] parameters, [CallerMemberName] string callerName = null) {
var overridesForMember = vtable[callerName];
var targetType = targetObj.GetType();
return (T) overridesForMember[targetType].Invoke(targetObj, parameters);
}
private static void InvokeOverride(object targetObj, object[] parameters, [CallerMemberName] string callerName = null) {
var overridesForMember = vtable[callerName];
var targetType = targetObj.GetType();
overridesForMember[targetType].Invoke(targetObj, parameters);
}
private static MixinAlphaProperties GetAdjunctProperties(IMixinAlpha target) {
MixinAlphaProperties result;
if (alphaEphemerons.TryGetValue(target, out result)) return result;
MixinAlphaProperties defaultProps = new MixinAlphaProperties();
alphaEphemerons.Add(target, defaultProps);
return defaultProps;
}
private static MixinBetaProperties GetAdjunctProperties(IMixinBeta target) {
MixinBetaProperties result;
if (betaEphemerons.TryGetValue(target, out result)) return result;
MixinBetaProperties defaultProps = new MixinBetaProperties();
betaEphemerons.Add(target, defaultProps);
return defaultProps;
}
#endregion
#region Alpha Impl
private class MixinAlphaProperties {
public int AlphaInt = 10;
public string AlphaString = "Hello!";
}
public static int GetAlphaInt(this IMixinAlpha @this) {
if (HasOverride(@this)) {
return InvokeOverride<int>(@this, new object[] { });
}
return GetAdjunctProperties(@this).AlphaInt;
}
public static void SetAlphaInt(this IMixinAlpha @this, int newValue) {
if (HasOverride(@this)) {
InvokeOverride(@this, new object[] { newValue });
return;
}
GetAdjunctProperties(@this).AlphaInt = newValue;
}
public static string GetAlphaString(this IMixinAlpha @this) {
if (HasOverride(@this)) {
return InvokeOverride<string>(@this, new object[] { });
}
return GetAdjunctProperties(@this).AlphaString;
}
public static void SetAlphaString(this IMixinAlpha @this, string newValue) {
if (HasOverride(@this)) {
InvokeOverride(@this, new object[] { newValue });
return;
}
GetAdjunctProperties(@this).AlphaString = newValue;
}
#endregion
#region Beta Impl
private class MixinBetaProperties {
public int BetaInt = 0;
public float BetaFloat = 0f;
}
public static int GetBetaInt(this IMixinBeta @this) {
if (HasOverride(@this)) {
return InvokeOverride<int>(@this, new object[] { });
}
return GetAdjunctProperties(@this).BetaInt;
}
public static void SetBetaInt(this IMixinBeta @this, int newValue) {
if (HasOverride(@this)) {
InvokeOverride(@this, new object[] { newValue });
return;
}
GetAdjunctProperties(@this).BetaInt = newValue;
}
public static float GetBetaFloat(this IMixinBeta @this) {
if (HasOverride(@this)) {
return InvokeOverride<float>(@this, new object[] { });
}
return GetAdjunctProperties(@this).BetaFloat;
}
public static void SetBetaFloat(this IMixinBeta @this, float newValue) {
if (HasOverride(@this)) {
InvokeOverride(@this, new object[] { newValue });
return;
}
GetAdjunctProperties(@this).BetaFloat = newValue;
}
public static bool BetaFoobar(this IMixinBeta @this) {
if (HasOverride(@this)) {
return InvokeOverride<bool>(@this, new object[] { });
}
var adjunctData = GetAdjunctProperties(@this);
return adjunctData.BetaFloat >= adjunctData.BetaInt * 2;
}
#endregion
}
Code snippet taken from "Simulating Multiple Inheritance In C#".