Я строю генератор Mixin.Я хотел бы знать, когда мне нужно избегать создания свойства для искажений, чтобы публичный API точно совпадал.Это важно для использования с Dynamic
, так как в противном случае существует несколько свойств с одним и тем же именем (если я просто пойду и добавлю свойства одно за другим), средство связывания типа времени выполнения просто выберет первое, которое может бытьтот, который я хочу.
Для методов я могу использовать InterfaceMapping
и словарь, чтобы не писать несколько методов пересылки для одного и того же метода.(Я просто говорю, что этот метод переопределяется тем же методом).
Таким образом, если, например, я смешиваю Dictionary<String,String>
, который реализует как IDictionary<string,string>
, так и обычный IDictionary
, мне нужно только открытое свойство String Item[String]
, а не Object Item[Object]
.
То же самое для EventHandlers, хотя я не думаю, что это большая проблема.
Прямо сейчас мой API создает свойство, только если базовое свойство или событие имеют новые методы, но когда я все еще смотрю на свой словарь, смешанный с объектом, у него все еще есть дублированные члены.
Вотсоответствующие фрагменты кода, весь класс довольно большой (и даже эти методы немного больше, чем мне нравится :() Драйвер добавляет все не специальные методы имен, найденные в карте интерфейса, затем проходит через каждое свойство и событие на картеи вызывает CreateMixinForwardingProperty/Event
. Я думаю, мне нужно отфильтровать те, которые реализованы явно в миксине, но как?
private static void CreateMixinForwardingEvent(TypeBuilder typeBuilder, FieldBuilder mixInField, EventInfo eventInfo, InterfaceMapping interfaceMap, Dictionary<MethodInfo, MethodBuilder> methodMap)
{
var indexOfAdd = Array.IndexOf(interfaceMap.InterfaceMethods, eventInfo.GetAddMethod());
bool createdNew;
var addMethodBuilder = CreateOrGetMixinForwardingMethod(
typeBuilder,
mixInField,
interfaceMap.InterfaceMethods[indexOfAdd],
interfaceMap.TargetMethods[indexOfAdd],
methodMap,
out createdNew
);
if (createdNew)
{
var eventBuilder = typeBuilder.DefineEvent(eventInfo.Name, eventInfo.Attributes, eventInfo.EventHandlerType);
eventBuilder.SetAddOnMethod(addMethodBuilder);
var indexOfRemove = Array.IndexOf(interfaceMap.InterfaceMethods, eventInfo.GetRemoveMethod());
eventBuilder.SetRemoveOnMethod(
CreateOrGetMixinForwardingMethod(
typeBuilder,
mixInField,
interfaceMap.InterfaceMethods[indexOfRemove],
interfaceMap.TargetMethods[indexOfRemove],
methodMap,
out createdNew
)
);
}
}
private static void CreateMixinForwardingProperty(TypeBuilder typeBuilder, FieldBuilder mixInField, PropertyInfo propertyInfo, InterfaceMapping interfaceMap, Dictionary<MethodInfo, MethodBuilder> methodMap)
{
PropertyBuilder propertyBuilder = null;// = typeBuilder.DefineProperty(propertyInfo.Name, propertyInfo.Attributes, propertyInfo.PropertyType,propertyInfo.GetIndexParameters().Select(p=>p.ParameterType).ToArray());
MethodBuilder propertyGetMethod = null;
MethodBuilder propertySetMethod;
bool createdNew;
if (propertyInfo.CanRead)
{
var indexOfGet = Array.IndexOf(interfaceMap.InterfaceMethods, propertyInfo.GetGetMethod());
propertyGetMethod = CreateOrGetMixinForwardingMethod(
typeBuilder,
mixInField,
interfaceMap.InterfaceMethods[indexOfGet],
interfaceMap.TargetMethods[indexOfGet],
methodMap,
out createdNew
);
if (createdNew)
{
propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name, propertyInfo.Attributes, propertyInfo.PropertyType, propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray());
propertyBuilder.SetGetMethod(propertyGetMethod);
}
}
if (propertyInfo.CanWrite)
{
var indexOfSet = Array.IndexOf(interfaceMap.InterfaceMethods, propertyInfo.GetSetMethod());
propertySetMethod = CreateOrGetMixinForwardingMethod(
typeBuilder,
mixInField,
interfaceMap.InterfaceMethods[indexOfSet],
interfaceMap.TargetMethods[indexOfSet],
methodMap,
out createdNew
);
if (createdNew)
{
if (propertyBuilder == null)
{
propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name, propertyInfo.Attributes, propertyInfo.PropertyType, propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray());
if (propertyInfo.CanRead) propertyBuilder.SetGetMethod(propertyGetMethod);
}
propertyBuilder.SetSetMethod(propertySetMethod);
}
}
}
private static MethodBuilder CreateOrGetMixinForwardingMethod(TypeBuilder typeBuilder, FieldBuilder mixinField, MethodInfo interfaceMethod, MethodInfo mixinMethod, Dictionary<MethodInfo, MethodBuilder> methodMap,out bool createdNew)
{
MethodBuilder overrideMethod;
if (createdNew = !methodMap.TryGetValue(mixinMethod, out overrideMethod))
{
overrideMethod = typeBuilder.DefineMethod(mixinMethod.Name, mixinMethod.Attributes, CallingConventions.HasThis, interfaceMethod.ReturnType, interfaceMethod.GetParameters().Select(p => p.ParameterType).ToArray());
var ilgen = overrideMethod.GetILGenerator();
LoadParameters(ilgen, 1);
ilgen.Emit(OpCodes.Ldfld, mixinField);
LoadParameters(ilgen, interfaceMethod.GetParameters().Length, 1);
ilgen.Emit(OpCodes.Callvirt, interfaceMethod);
ilgen.Emit(OpCodes.Ret);
methodMap.Add(mixinMethod, overrideMethod);
}
typeBuilder.DefineMethodOverride(overrideMethod, interfaceMethod);
return overrideMethod;
}
Я знаю, что мне не нужно реализовывать свойства или события, которые реализованы только явнокак при приведении к типу интерфейса, они будут работать правильно, а динамический просмотр только общедоступных членов.
edit: Поскольку я удалил комментарии, чтобы немного упростить вставку, генератор добавляет поле длясмешанный в элементе для переопределения базового типа, и для каждого метода, который яmplements просто загружает поле mixin и вызывает метод со свойством mixin.Я называю этот процесс пересылкой сообщений, отсюда и название.