Вызов SetValue, GetValue в InstanceType с параметрами
От: Nonick  
Дата: 14.02.08 10:47
Оценка:
Здравствуйте!

При присвоении или обращении к свойству помеченному атрибутом InstanceType(typeof(Type1)) происходит перенаправление к указанному типу, при этом в этом типе ищется своиство с атрибутами GetValue/SetValue, если нет такого поля (св-ва), то ищется поле с именем Value. Вот хотелось бы, чтобы в случае ненахождения и такого поля (св-ва) искался бы метод GetValue, SetValue, для начала соответственно без параметров, потом для с 1 параметром и с 2 параметрами. Первый параметр — это, соответственно, тип Parent-а, в котором находится инстанцируемый тип, а второй typeof(PropertyInfo) соответствующего свойства для которого он строится.
Конечно можно было бы запоминать ссылку на парента и тип свойства при конструировании типа, но не хотелось бы хранить лишние данные.

Сейчас сделан класс InstanceTypeBuilderEx, который в целом дублирует InstanceTypeBuilder с дополнениями в выделенных местах, а также практически скопипастины InstanceTypeExAttribute.cs и GlobalInstanceTypeExAttribute.cs чтоб возвращали InstanceTypeBuilderEx.

В InstanceTypeBuilderEx написал следующее


    protected override void BuildAbstractSetter()
        {
            FieldBuilder field        = GetField();
            EmitHelper   emit         = Context.MethodBuilder.Emitter;
            Type         propertyType = Context.CurrentProperty.PropertyType;
            MemberInfo   setter       = GetSetter();

            if (InstanceType.IsValueType) emit.ldarg_0.ldflda(field);
            else                          emit.ldarg_0.ldfld (field);

            emit.ldarg_1.end();

            if (setter is PropertyInfo)
            {
                PropertyInfo pi = ((PropertyInfo)setter);

                if (propertyType.IsValueType && !pi.PropertyType.IsValueType)
                    emit.box(propertyType);

                if (InstanceType.IsValueType) emit.call    (pi.GetSetMethod());
                else                          emit.callvirt(pi.GetSetMethod());
            } 
                       else if (setter is FieldInfo)
                       {
                         FieldInfo fi = (FieldInfo)setter;

                        if (propertyType.IsValueType && !fi.FieldType.IsValueType)
                          emit.box(propertyType);

                        emit.stfld(fi);
      } 
      else  {
        MethodInfo mi = (MethodInfo)setter;
        ParameterInfo[] api = mi.GetParameters();

        if (propertyType.IsValueType && !api[0].ParameterType.IsValueType)
          emit.box(propertyType);

        if (api.Length == 2)
          emit
            .ldarg_0
          .end();

        if (api.Length == 3) {
          FieldBuilder ifb = GetPropertyInfoField();
          emit
            .ldarg_0
            .ldsfld(ifb)
          .end();
        }

        if (InstanceType.IsValueType) emit.call(mi);
        else                          emit.callvirt(mi);      }
        }




И соответственно GetSetter выглядит след. образом.


private MemberInfo GetSetter()
        {
            Type propertyType = Context.CurrentProperty.PropertyType;

            FieldInfo[] fields = InstanceType.GetFields(
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetField);

            foreach (FieldInfo field in fields)
            {
                object[] attrs = field.GetCustomAttributes(typeof(SetValueAttribute), true);

                if (attrs.Length > 0 && TypeHelper.IsSameOrParent(field.FieldType, propertyType))
                    return field;
            }

            PropertyInfo[] props = InstanceType.GetProperties(
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);

            foreach (PropertyInfo prop in props)
            {
                object[] attrs = prop.GetCustomAttributes(typeof(SetValueAttribute), true);

                if (attrs.Length > 0 && TypeHelper.IsSameOrParent(prop.PropertyType, propertyType))
                    return prop;
            }

            foreach (FieldInfo field in fields)
                if (field.Name == "Value" && TypeHelper.IsSameOrParent(field.FieldType, propertyType))
                    return field;

            foreach (PropertyInfo prop in props)
                if (prop.Name == "Value" && TypeHelper.IsSameOrParent(prop.PropertyType, propertyType))
                    return prop;

//Если не нашли свойств, ищем методы

      try {
        MethodInfo method = null;

        method = InstanceType.GetMethod("SetValue", new Type[] { Context.CurrentProperty.PropertyType });
        if (method != null)
        {
          return method;
        }

        method = InstanceType.GetMethod("SetValue", new Type[] { Context.CurrentProperty.PropertyType, Context.Type.Type });
        if (method != null)
        {
          return method;
        }

        method = InstanceType.GetMethod("SetValue", new Type[] { Context.CurrentProperty.PropertyType, Context.Type.Type, typeof(PropertyInfo) });
        if (method != null)
        {
          return method;
        }
      } catch (AmbiguousMatchException) {

      } 
            throw new TypeBuilderException(string.Format(
                "The '{0}' type does not have appropriate setter. See '{1}' member of '{2}' type.",
                InstanceType.FullName,
                propertyType.FullName,
                Context.Type.FullName));
        }


BuildAbstractGetter и GetGetter аналогичны.

Хотелось бы знать мнение Игоря, Павла а также всех заинтересованных по поводу того, не закинуть ли такую фичу в BLT. Спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.