2010-03-19 8 views
11

Estoy experimentando con DynamicObject. Una de las cosas que trato de hacer es establecer los valores de ref/out argumentos, como se muestra en el siguiente código. Sin embargo, no puedo tener los valores de i y j en Main() configurados correctamente (aunque están configurados correctamente en TryInvokeMember()). ¿Alguien sabe cómo llamar a un objeto DynamicObject con argumentos ref/out y ser capaz de recuperar los valores establecidos dentro del método?C# 4.0 'dinámico' no establece argumentos de 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; 
    } 
} 

actualización 7/15: Microsoft afirma haber solucionado el problema para la próxima versión de .NET http://connect.microsoft.com/VisualStudio/feedback/details/543101/net-4-0s-dynamicobject-doesn-t-set-ref-out-arguments

Actualización 08/09/2012: Probado utilizando VS.NET 2012 con .NET 4.0 y 4.5, confirme: ya está arreglado.

+0

Duplicado exacto: http: // stackoverflow.com/questions/2268857/c-4-determination-parameter-passing-semántica-in-dynamic-calls – Gabe

+0

@gabe: En realidad, vi esa pregunta, pero esa pregunta era si se podía saber si un parámetro se podía conocer como aprobado por ref/out o no, que es completamente diferente de lo que estoy preguntando aquí. No estoy interesado en eso en mi caso porque puedo pensar con un poco de reflexión en la clase 'Target'. –

+0

Aunque su pregunta es diferente, la respuesta a eso incluye la respuesta a su pregunta: * DynamicObject es "call-by-value" * – Gabe

Respuesta

5

Esto se parece a podría ser un error - probablemente en DynamicObject. Si se agrega un método Wrap a Proxy así:

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

A continuación, a pesar de esto todavía se llama de forma dinámica (es decir, el código en Main se mantiene igual) funciona el código ... así que al menos que el general "cómo lo hace una capa de "trabajo dinámico de objetos" admite el paso por referencia.

sospecho si esto es de hecho un error en el DLR, puede ser demasiado tarde para arreglar para .NET 4 - pero vale la pena informar sobre Connect de todos modos por lo que se puede fijar en un service pack. Alternativamente, si se trata de una restricción/limitación deliberada, debe estar claramente documentada en MSDN (que por el momento no está, por lo que puedo ver).

+0

Solución interesante, gracias. Es bueno saberlo, pero obviamente no recurriría a DynamicObject si tuviera que implementar cada método de reenvío :). He enviado un informe de error a Connect según su sugerencia. –

+0

No veo cómo podría ser un error porque no hay forma de que funcione. 'TryInvokeMember' toma una matriz de objetos para los argumentos. Para colocar los enteros en la matriz, deben estar enmarcados, que los copia. La única forma en que esto podría incluso funcionar a medias es si el encuadernador dinámico copiado se remite desde la matriz original de args una vez que se completa la llamada, pero eso es solo simular referencias. – Gabe

+1

@gabe: así es como funciona cuando se llama a un método con parámetros de ref utilizando la reflexión, y como ya he dicho, funciona cuando el método se proporciona estáticamente aunque todavía se * llame * dinámicamente. En otras palabras, funciona en el nivel DynamicMetaObject. Claro, tendría algunas fallas en comparación con el pase "verdadero" por referencia (por ejemplo, las variables se desasociarían durante la llamada al método en sí), pero en el 99% de los casos estoy seguro de que no importaría. Parece que sería preferible ignorar el hecho de que son parámetros de ref completamente, como lo es ahora. –

2

Para acortar la historia, DynamicObject no admite el paso por referencia, por lo que lo que desea hacer no es directamente posible.

5

Esto no es un error. Como ya se dijo aquí, DynamicObject no admite parámetros de ref y out en TryInvokeMember. Todo lo que se pasa a este método se trata "por valor". En breve, el método TryInvokeMember simplemente ignora estas palabras clave, y es por eso que su método no funciona.

Si sigue la sugerencia de Jon Skeet y crea su propio método de ajuste dentro de una clase heredada de DynamicObject, este será un escenario un poco diferente. El flujo de trabajo tiene este aspecto: cuando hay una llamada a un método para DynamicObject, el cuaderno de ejecución de C# busca primero el método en la clase misma. Si puede encontrar uno, llama a este método. En este punto, la información sobre los parámetros "ref" y "out" aún se conserva. Si no puede encontrar dicho método, llama al método TryInvokeMember y simplemente arroja información sobre las palabras clave "ref" y "out" y comienza a tratar todo como "por valor". Recuerde que DynamicObject tiene que soportar interoperabilidad con otro idioma, que podría no tener todas las características de C#.

Es cierto, la información sobre "ref" y "out" ahora no se encuentra en la documentación. Lo agregaré a la próxima actualización de documentación.

+0

¡Buena información, gracias! Sin embargo, aunque DynamicObject no debería molestarse en ref/out para la interoperabilidad, la persona que llama de TryInvokeMember (que es la carpeta de C# según entiendo de su explicación), debería molestarse y tratar de establecer la salida/ref basándose en la matriz args establecida en el interior TryInvokeMember. ¿No podría? ¿No debería? –

Cuestiones relacionadas