2016-12-28 2 views
1

Я пытаюсь написать некоторый .NET-код, который динамически генерирует различные типы из списка простых интерфейсов, содержащих только средства определения свойств/геттеры. Один из типов, которые я хотел бы генерировать из списка, - это тот, который поддерживает данные интерфейсы (с автоматически генерируемыми полями поддержки), но также отслеживает все операции набора свойств, записывая, какие свойства были изменены (и в будущем, возможно, время, контекст, пользователь и т. д., которые внесли изменения). После создания образцов в C# и изучения их с помощью ILDASM, у меня есть работа, но есть одна часть кода, для которой IL выглядит намного сложнее, чем должно быть. Для этого типа внутри кода, который определяет установщик для свойства, я устанавливаю значение в поле (достаточно просто), но тогда мне нужно получить PropertyInfo для свойства, которое я устанавливаю (так что позже пользователь может перечислить PropertyInfo s для свойств, которые были изменены). Для того, чтобы получить PropertyInfo для свойства которого сеттер я нахожусь в середине определения, у меня есть следующий код (средняя секция):C# Получение PropertyInfo в setter с помощью PropertyBuilder

... 
PropertyBuilder pb = tb.DefineProperty(property.Name, property.Attributes, property.PropertyType, null); 
MethodAttributes attr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual; 
MethodBuilder setmb = tb.DefineMethod(SetPropertyMethodPrefix + property.Name, attr, null, new Type[] { property.PropertyType }); 
ILGenerator setgen = setmb.GetILGenerator(); 
... 
// store the value in the backing field 
setgen.Emit(OpCodes.Ldarg_0); 
setgen.Emit(OpCodes.Ldarg_1); 
setgen.Emit(OpCodes.Stfld, fb); 
// get ready to give the change tracker the PropertyInfo for this property 
setgen.Emit(OpCodes.Ldarg_0); 
setgen.Emit(OpCodes.Ldfld, changeTrackerField); 


// get the PropertyInfo for this property (there has to be a better way!) 
setgen.Emit(OpCodes.Ldarg_0); 
setgen.Emit(OpCodes.Call, typeof(object).GetMethod("GetType")); 
// alternatively, instead of the two lines above, I can get the type token directly and get the type from there, which may or may not be any faster... 
// setgen.Emit(OpCodes.Ldtoken, tb.TypeToken.Token); 
// setgen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) })); 
setgen.Emit(OpCodes.Ldstr, property.Name); 
setgen.Emit(OpCodes.Callvirt, typeof(Type).GetMethod("GetProperty", new Type[] { typeof(string) })); 


// give the PropertyInfo to our change tracking object 
setgen.Emit(OpCodes.Callvirt, typeof(ChangeTracker).GetMethod("MarkPropertyChanged")); 
setgen.Emit(OpCodes.Ret); 

В средней части эквивалентна кода C#: GetType().GetProperty(<propertyName>) , но кажется, что должен быть какой-то более быстрый способ получить PropertyInfo за собственность, которую я нахожу посреди здания. Версия GetTypeFromHandle кажется, что она может быть более эффективной (typeof (T) vs. this.GetType()), но я предполагаю, что есть способ обойти GetProperty вообще, возможно, используя PropertyBuilder.PropertyToken.

Есть ли способ получить экземпляр PropertyInfo для объекта? Я нахожусь посередине здания, не прибегая к вставке кода IL, который делает саморефлексию?

+0

Я не понимаю, почему вы не используете стандартные методы отражения и пытаетесь написать код на основе сгенерированного кода IL. Сказав это, отражение дает вам гибкость, но оно имеет более низкую производительность. Кроме того, IObservable - очень хороший встроенный трекер изменений. – Sparrow

+0

IObservable - очень хороший трекер изменений **, если вы работаете в том же процессе **. В этом случае я хочу иметь возможность запускать удаленно. – James

+0

Достаточно честный. Вы используете дженерики?Вы можете написать что-то вроде этого (что, как я считаю, не имеет лучшей производительности): var props = typeof (T) .GetProperties(); – Sparrow

ответ

0

Я понимаю, что можно было бы подумать, что есть какой-то способ ссылаться на свойство (или событие) через его токен в теле метода, но это невозможно. Единственный жизнеспособный код операции будет ldtoken, но только принимает методы, поля или типы. Я вижу ваши проблемы, но я бы посоветовал сначала сделать некоторые тесты, чтобы узнать, действительно ли вызывает недостаток в вызове GetProperty. Однако у вас есть несколько других вариантов для ссылки на свойство.

  • Переверните все свойства и проверьте их токен (испускаемый как целое число в CIL).

  • Использование словаря в качестве карты из маркера объекта в фактическое PropertyInfo. Заполнять словарь в конструкторе или лениво.

  • Запомните заказ, в котором вы строите свойства, и используйте его для индексирования массива, возвращаемого GetProperties. Я полагаю, что порядок должен быть таким же, но лучше проверить его.

0

Для решения подобных проблем в прошлом я создал статически доступный Словарь с длинным существованием простого, с атомарно увеличивающимся уникальным идентификатором. Затем вы можете получить PropertyInfo - или любой другой объект - используя вызов статического метода с созданным вами токеном, жестко закодированным в динамический метод.

Смежные вопросы