开发者

Is there an equivalent to InterfaceMapping for properties & Events?

开发者 https://www.devze.com 2023-01-26 13:56 出处:网络
I\'m building a Mixin generator. I\'d like to know when I have to avoid building a property for mangling purposes so that the public API matches exactly. This is important for usage with Dynamic, as o

I'm building a Mixin generator. I'd like to know when I have to avoid building a property for mangling purposes so that the public API matches exactly. This is important for usage with Dynamic, as otherwise since there are multiple properties with the same name, (if I just go and add the properties one by one), the runtime type binder just chooses the first one, which may not be the one I want.

For methods, I can use InterfaceMapping and a dictionary so that I don't write multiple method forwarders for the same method. (I just say that this method is overriden by the same method).

So if for instance I mix-in a Dictionary<String,String> which implements both IDictionary<string,string> and regular IDictionary, I only want the public facing String Item[String] property, not Object Item[Object] property.

Same deal for EventHandlers, although I don't think its as much of a problem.

Right now my API creates a property only if the underlying property or event have new methods, but when I still look at my dictionary mixed in object it still has duplicated members.

Here is the relevant code snippets, the whole class is quite large (and even these methods are a little bigger than I like :() The driver adds all the not special name methods found in the interface map, then goes through each property and event on the map and calls the CreateMixinForwardingProperty/Event. I guess I have to filter the ones that implemented explicitly on the mixin but how?

    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],
                 开发者_JS百科   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;
    }

I know I don't have to implement the properties or events that only implemented explicitly as when cast to the interface type they will work correctly, and dynamic only looks at publicly available members.

edit: Since I stripped out comments to make this a little bit easier to paste the generator adds a field for the mixed in member to an override of the base type, and for each method that it implements it simply loads the mixin field, and invokes the method on the mixin property. I call this process message forwarding, hence the name.

0

精彩评论

暂无评论...
验证码 换一张
取 消