Здравствуйте!
При присвоении или обращении к свойству помеченному атрибутом 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. Спасибо.