Может ли аргумент «this» метода экземпляра быть нетипизированным (то есть System.Object)? - PullRequest
4 голосов
/ 26 апреля 2011

Я использую System.Reflection.Emit TypeBuilder для генерации нескольких пользовательских классов .NET с методами экземпляра.Например:

public class EmittedClass
{
    public bool TryGetName(out string value)
    {
        ...
    }

    public bool TryGetAge(out int value)
    {
        ...
    }
}

Все методы имеют одну и ту же сигнатуру, которая может быть описана общим делегатом:

public delegate bool TryGetter<T>(out T value);

Конечно, я хотел бы иметь возможность явно указать цельэкземпляр на сайте вызова, например:

var instance = InstanceFactory.CreateInstance();
var tryGetName = InstanceFactory.CreateTryGetter<string>("Name");
string name;
if (tryGetName(instance, out name)) // Problem here.
{
    ...
}

Чтобы это работало, мне нужно превратить делегата в так называемый открытый делегат :

public delegate bool TryGetter<T>(object instance, out T value);

Поскольку у меня нет типа времени компиляции целевого экземпляра, мне нужно передать его как System.Object.Однако, это прерывается во время выполнения, потому что метод экземпляра ожидает, что его 'this' будет иметь тип декларирующего класса.Ой.

Решение, которое я сейчас использую, заключается в создании промежуточного лямбда-выражения, которое принимает входной объект, выполняет приведение во время выполнения к целевому типу, а затем переходит к вызову целевого метода.Это работает, но я чувствую себя неловко из-за этого клуджа в середине.

Вопрос в том, могу ли я каким-то образом изменить мои испускаемые методы так, чтобы их this аргументы принимали любые System.Object и до сих порсохранить проверяемый код?

Если нет, я думаю, что я все еще могу избежать промежуточной лямбды, сделав методы статичными и выполняя приведение в их тела.В идеале, однако, я бы хотел вообще избежать кастинга, но я подозреваю, что в управляемом мире этого не может быть из-за того, как работает проверка CIL и загрузка метаданных.

Просто интересно, не знает ли кто-нибудь больше о CILможете дать мне совет на эту тему.Спасибо!

ОБНОВЛЕНИЕ: Проблема, которую я пытаюсь решить

Классы имеют свойства, которые обычно поддерживаются полями.Это хорошо, если ожидается, что экземпляры указанных классов будут поддерживать только одно состояние за один раз.Под состоянием я подразумеваю комбинацию значений в полях экземпляра в определенный момент времени.

Моим бизнес-требованием является внедрение альтернативного хранилища, чем простых полей, которое позволит одному экземпляру класса иметь более одного состояния одновременновремя.Побочной целью этого требования является сделать это как можно более эффективным как с точки зрения памяти, так и с точки зрения скорости доступа.

Основная идея моего подхода заключается в создании зеркального отображения бизнес-класса с использованием * 1040.* и бизнес-класс должен поддерживать набор этих экземпляров, каждый из которых соответствует определенному состоянию.Естественно, методы получения и установки бизнес-класса должны быть подключены к соответствующим методам в экземпляре состояния.Здесь задействовано гораздо больше деталей, но это основная идея.

Я надеюсь, что объяснение поможет понять причину, по которой я задаю этот вопрос.Я ценю, что многим это может показаться чрезмерной разработкой, но другие альтернативы, которые не включают System.Reflection.Emit, слишком медленны и требуют много ресурсов.Я не могу этого.

Ответы [ 4 ]

2 голосов
/ 10 мая 2011

Вам не нужно представлять тип 'this' в сигнатуре делегата.

То есть определение делегата выглядит так:

public delegate bool TryGetter<T>(out T value);

Любая переменная в форме:

TryGetter<T> x;

Может содержать:

  1. Метод экземпляра для типа R с подписью bool Foo(out T value) вместе с экземпляром объекта типа R.
  2. Aстатический метод с подписью static bool Foo(out T value)
  3. Статический метод с подписью static bool Foo(R object, out T value) для заданного экземпляра объекта типа R.

Третья форма называется делегированием каррирования и допускает статическоеметод с N + 1 аргументом, ведущий себя так, как если бы это был метод экземпляра с N аргументами (однако карри может быть только первый аргумент).

Итак, вам нужен интерфейс:

var instance = InstanceFactory.CreateInstance();
var tryGetName = InstanceFactory.CreateTryGetter<string>(instance,"Name");

Затем вы можете сделать: tryGetName() для возврата значения.

Возможно, вы захотите перейти к случаю № 3, где вы сгенерировали DynamicMethod с подписью bool TryGetWhatEver(TheTypeOfInstance obj, out WhatEver x), а затем создатьTryGetter<WhatEver>.


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

2 голосов
/ 26 апреля 2011

Учитывая ваши требования, не будет ли библиотека, подобная ValueInjecter или AutoMapper , сократить весь стандартный код копирования из одного крупного бизнес-объекта в разные объекты состояния?Даже если это не совсем то, что вы хотите, возможно, они могут послужить источником вдохновения для вашей задачи.

1 голос
/ 10 мая 2011

Reflection.Emit - это излишнее излишнее создание внутреннего механизма хранения для размещения копий значений ваших свойств.Что-то простое, например, словарь, было бы достаточным способом хранения различных «состояний» отображений значений name-> value.

0 голосов
/ 10 мая 2011

Просто используйте dynamic:

dynamic instance = InstanceFactory.CreateInstance();
var tryGetName = InstanceFactory.CreateTryGetter<string>("Name");
string name;

// Should work if “instance” is of the right type *at runtime*
if (tryGetName(instance, out name))
{
    ...
}
...