С# 4.0 'dynamic' не устанавливает аргументы ref/out

Я экспериментирую с DynamicObject. Одна из вещей, которую я пытаюсь сделать, это установить значения аргументов ref/out, как показано в приведенном ниже коде. Однако я не могу правильно установить значения i и j в Main() (даже если они правильно установлены в TryInvokeMember()). Кто-нибудь знает, как вызвать объект DynamicObject с аргументами ref/out и иметь возможность извлекать значения, установленные внутри метода?

class Program
{
    static void Main(string[] args)
    {
        dynamic proxy = new Proxy(new Target());
        int i = 10;
        int j = 20;
        proxy.Wrap(ref i, ref j);
        Console.WriteLine(i + ":" + j); // Print "10:20" while expect "20:10"
    }
}

class Proxy : DynamicObject
{
    private readonly Target target;

    public Proxy(Target target)
    {
        this.target = target;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        int i = (int) args[0];
        int j = (int) args[1];
        target.Swap(ref i, ref j);
        args[0] = i;
        args[1] = j;
        result = null;
        return true;
    }
}

class Target
{
    public void Swap(ref int i, ref int j)
    {
        int tmp = i;
        i = j;
        j = tmp;
    }
}

Обновление 7/15: Microsoft утверждает, что исправила проблему для следующей версии .NET http://connect.microsoft.com/VisualStudio/feedback/details/543101/net-4-0s-dynamicobject-doesn-t-set-ref-out-arguments

Обновление 9/8/2012: Протестировано с использованием VS.NET 2012 с .NET 4.0 и 4.5, подтвердите: он уже исправлен.

Ответ 1

Похоже, это может быть ошибка - возможно, в DynamicObject. Если вы добавили метод Wrap к Proxy следующим образом:

public void Wrap(ref int x, ref int y)
{
    target.Swap(ref x, ref y);
}

Тогда, хотя это все еще называется динамически (т.е. код в Main остается неизменным), код работает... так что по крайней мере общий уровень "как работает динамический объект" поддерживает pass-by-reference.

Я подозреваю, что это действительно ошибка в DLR, возможно, слишком поздно исправить для .NET 4 - но стоит сообщить Connect, так что это может быть исправлено в пакете обновления. В качестве альтернативы, если это преднамеренное ограничение/ограничение, оно должно быть четко задокументировано в MSDN (которого я пока не вижу, насколько я вижу).

Ответ 2

Это не ошибка. Как уже было сказано здесь, DynamicObject не поддерживает параметры ref и out в TryInvokeMember. Все, переданное этому методу, обрабатывается "по значению". Вскоре метод TryInvokeMember просто игнорирует эти ключевые слова, и поэтому ваш метод не работает.

Если вы выполните предложение Jon Skeet и создадите свой собственный метод Wrap в классе, унаследованном от DynamicObject, это будет немного другой сценарий. Рабочий процесс выглядит так: когда есть вызов метода для DynamicObject, связующее средство времени выполнения С# сначала ищет метод в самом классе. Если он может найти один, он вызывает этот метод. На данный момент информация о параметрах "ref" и "out" сохраняется. Если он не может найти такой метод, он вызывает метод TryInvokeMember и просто выводит информацию о словах "ref" и "out" и начинает обрабатывать каждое значение как "по значению". Помните, что DynamicObject должен поддерживать совместимость с другим языком, который может не иметь всех функций С#.

Правда, информация о "ref" и "out" отсутствует в документации. Я добавлю его к следующему обновлению документации.

Ответ 3

Чтобы сделать длинный рассказ коротким, DynamicObject не поддерживает pass-by-reference, поэтому то, что вы хотите сделать, напрямую не возможно.