Вызов 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. Спасибо.
Re: Вызов SetValue, GetValue в InstanceType с параметрами
От: Блудов Павел Россия  
Дата: 14.02.08 12:50
Оценка:
Здравствуйте, Nonick, Вы писали:

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

А можно подробней, откуда такая надобность появилась?
Вроде как GetValue+SetValue в .net не считается за comme il faut?
Наверное старый добрый COM interop?
... << RSDN@Home 1.2.0 alpha rev. 788>>
Re[2]: Вызов SetValue, GetValue в InstanceType с параметрами
От: Nonick  
Дата: 14.02.08 14:02
Оценка:
Здравствуйте, Блудов Павел, Вы писали:

БП>Здравствуйте, Nonick, Вы писали:


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

БП>А можно подробней, откуда такая надобность появилась?
БП>Вроде как GetValue+SetValue в .net не считается за comme il faut?
БП>Наверное старый добрый COM interop?

Нет, все проще.
Приведу пример, из него, думаю, будет понятно




class EntityBase{

  [InstanceType(typeof(ValueBox<int>))]
  public abstract int IntProp { get; set; }
}


struct ValueBox<T>  {
  private T _value;

  [GetValue]
  T GetValue() { return _value; }
  //или так T GetValue(EntityBase parent) { ... например LazyLoad, но нужен parent для доступа к сервису}
  //или так T GetValue(EntityBase parent, PropertyInfo pi) { }
  

  [SetValue]
  SetValue(T value) { _value = value; }
  //или так SetValue(T value, EntityBase parent) { }
  //или так SetValue(T value, EntityBase parent, PropertyInfo pi) { ... например Undo, но нужен parent для доступа к сервису}


}


Соответственно, та версия функции, которую Builder обнаружил, она и будет вызвана. Причем, это происходит после стандартного поиска (полей, свойств), который реализован сейчас.
Re[3]: Вызов SetValue, GetValue в InstanceType с параметрами
От: Nonick  
Дата: 15.02.08 13:19
Оценка:
Павел, разве из примера не видно насколько это может быть удобным?
Re[4]: Вызов SetValue, GetValue в InstanceType с параметрами
От: Блудов Павел Россия  
Дата: 16.02.08 01:16
Оценка:
Здравствуйте, Nonick, Вы писали:

N>Павел, разве из примера не видно насколько это может быть удобным?

Я не могу понять, почему вы настаиваете на паре GetValue/SetValue?
Почему вы категорически не хотите использовать свойства?
У вас что, в самом деле принят такой стиль кодирования
См. http://msdn2.microsoft.com/en-us/library/ms182181(VS.80).aspx
и файл Source\Doc\Development rules and regulations.doc поставляемый с BLToolkit.
... << RSDN@Home 1.2.0 alpha rev. 788>>
Re[5]: Вызов SetValue, GetValue в InstanceType с параметрами
От: Nonick  
Дата: 17.02.08 19:01
Оценка:
Здравствуйте, Блудов Павел, Вы писали:

БП>Здравствуйте, Nonick, Вы писали:


N>>Павел, разве из примера не видно насколько это может быть удобным?

БП>Я не могу понять, почему вы настаиваете на паре GetValue/SetValue?
БП>Почему вы категорически не хотите использовать свойства?
БП>У вас что, в самом деле принят такой стиль кодирования
БП>См. http://msdn2.microsoft.com/en-us/library/ms182181(VS.80).aspx
БП>и файл Source\Doc\Development rules and regulations.doc поставляемый с BLToolkit.

Ну, мне кажется это вполне логичным, что если я пытаюсь присвоить свойству некое значение, то метод может называться SetValue, но если это вам кажется не совсем комильфо, то не вопрос, можно переименовать, только во что... Предложите, плиз, что вам кажется более подходящим. Буду рад.
Но по-моему, это не самое главное. В целом-то, как, разумно?
Re[6]: Вызов SetValue, GetValue в InstanceType с параметрами
От: Nonick  
Дата: 18.02.08 08:37
Оценка: 15 (1)
Здравствуйте, Nonick, Вы писали:

N>Здравствуйте, Блудов Павел, Вы писали:


БП>>Здравствуйте, Nonick, Вы писали:


N>>>Павел, разве из примера не видно насколько это может быть удобным?

БП>>Я не могу понять, почему вы настаиваете на паре GetValue/SetValue?
БП>>Почему вы категорически не хотите использовать свойства?

Павел, боюсь что Вы не совсем правильно поняли суть моего предложения.
Ключевой момент моего предложения — "с параметрами". Естественно, мне не нужна тривиальная пара GetValue()/SetValue(T value).
Вот пример, близкий к тому, что я использую, дан вариант без моих "расширений" и с моими "расширениями"


  [GlobalInstanceType(typeof(int),      typeof(ValueBox<int>))]
  [GlobalInstanceType(typeof(string),   typeof(ValueBox<string>), "")]
  // Ну и для остальных типов тоже

  [ImplementInterface(typeof(ISetParent))] //В моем варианте не понадобится
  public class EntityBase
  {
    EntityBase() {
      (this as ISetParent).SetParent(this, null); //В моем варианте не понадобится
    }
    internal void OnPropertyChanging<T>(T oldValue, string propertyName) {
      //Здесь хочется именно типизированный вариант, нужен для сервиса Undo
    }
    
  }

  //Это как я могу сделать сейчас
  public struct ValueBox<T> : ISetParent
  {
    private EntityBase    _parent;
    private PropertyInfo  _propertyInfo;
    private T             _value;
    
    [GetValue, SetValue]
    public T Value 
    {
      get { 
        return _value;
      }
      set {
        _parent.OnPropertyChanging(_value, _propertyInfo.Name);
        _value = value;
      }
    }
    
    #region ISetParent Members    
    public void SetParent(object parent, PropertyInfo propertyInfo)
    {
      _parent       = (EntityBase)parent;
      _propertyInfo = propertyInfo;
    }
    #endregion
  }

  //Это как я хочу
  public struct ValueBox<T>
  {
    private T _value;
    
    [GetValue]
    public T GetValue() 
    {
      return _value;
    }
    [SetValue]
    public void SetValue(T value, EntityBase parent, PropertyInfo propertyInfo)
    {
      parent.OnPropertyChanging(_value, propertyInfo.Name);
      _value = value;
    }
  }


Т.о. структура для хранения значения занимает место ровно под хранение этого значения,
а в методах присваивания и отдачи значения я могу взаимодействовать с parent-объектом
не храня на него постоянную ссылку в моей структуре.
Done
От: Блудов Павел Россия  
Дата: 25.02.08 10:26
Оценка:
Здравствуйте, Nonick, Вы писали:

N>Павел, боюсь что Вы не совсем правильно поняли суть моего предложения.

N>Ключевой момент моего предложения — "с параметрами". Естественно, мне не нужна тривиальная пара GetValue()/SetValue(T value).
N>Вот пример, близкий к тому, что я использую, дан вариант без моих "расширений" и с моими "расширениями"

Добавлено в ревизии 511. См. UnitTests\CS\TypeBuilder\GetSetValueAttributeTest.cs
... << RSDN@Home 1.2.0 alpha rev. 788>>
Re: Done
От: IT Россия linq2db.com
Дата: 26.02.08 03:29
Оценка:
Здравствуйте, Блудов Павел, Вы писали:

БП>Добавлено в ревизии 511. См. UnitTests\CS\TypeBuilder\GetSetValueAttributeTest.cs


А как определяется, что параметр является parent? У нас есть атрибут ParentAttribute для параметров, надо бы его задействовать. Да и PropertyInfoAttribute для propertyInfo тоже хорошо бы было использовать. Как в ImplementInterfaceBuilder.
... << RSDN@Home 1.2.0 alpha rev. 771>>
Если нам не помогут, то мы тоже никого не пощадим.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.