2012-09-28 18 views
5

Creación de una clase que implementa DynamicObject¿Se puede obtener el valor encadenado de DynamicObject?

public class Test : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     if (binder.Name == ("Posts")) 
     { 
      result = "property accessed was 'Posts'"; 
      return true; 
     } 

     return base.TryGetMember(binder, out result); 
    } 
} 

puedo llamar

dynamic test = new Test(); 
var result = test.Posts; 

Y el valor de result es "dinámico prueba nueva = Test(); var result = test.Posts;"

Eso está bien.

Lo que me pregunto es, cuando se invoca TryGetMember es posible obtener el valor encadenado.

Así que si me llama:

dynamic test = new Test(); 
var result = test.Posts.Load(123); 

que puede luego hacer algo como:

if (binder.Name == ("Posts")) 
{ 
    if (... == "Load") 
     result = this.Load<Post>(... 123); 
    return true; 
} 

es algo así como que sea posible? No puedo encontrar una manera de hacerlo.

hasta ahora tengo:

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic test = new Test(); 
     dynamic result = test.Posts.Load(123); 

     Console.WriteLine(result.Name); 

     dynamic result2 = test.Posts.Load(909); 

     Console.WriteLine(result2.Name); 

     Console.ReadKey(); 
    } 
} 

public class Test : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     if (binder.Name == ("Posts")) 
     { 
      result = new ChainBuilder(this, "Post"); 
      return true; 
     } 

     return base.TryGetMember(binder, out result); 
    } 

    public T Load<T>(int id) where T : Post, new() 
    { 
     if (id == 123) 
      return new T {Id = 123, Name = "Bananas"}; 

     return new T {Id = 0, Name = "Others"}; 
    } 

    private class ChainBuilder : DynamicObject 
    { 
     public dynamic OriginalObject { get; set; } 
     public string PropertyInvoked { get; set; } 

     public ChainBuilder(DynamicObject originalObject, string propertyInvoked) 
     { 
      OriginalObject = originalObject; 
      PropertyInvoked = propertyInvoked; 
     } 

     public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 
     { 
      if (binder.Name == "Load") 
      { 
       result = OriginalObject.Load<Post>((int)args[0]); 
       return true; 
      } 

      return base.TryInvokeMember(binder, args, out result); 
     } 
    } 
} 

public class Post 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Cuál es gracias a Bartosz.

Pero parece que es básicamente lo que Marc ha suministrado.

¡Déme un buen punto de partida! Dejaré esto abierto por ahora para cualquier otra sugerencia.

Esta cuestión ha dado lugar a

No

un proyecto real, sólo la creación de prototipos, pero logrado lo que queríamos.

Respuesta

3

Cada paso de la evaluación es independiente; no evalúa .Posts.Load(123) - evalúa .Posts, y luego evalúa por separado .Load(123), entonces no: no puede hacer esto en un solo paso. El truco consiste en componer los valores usted mismo, por ejemplo:

using System; 
using System.Dynamic; 
using System.Text; 
static class Program { 
    static void Main() { 
     dynamic test = new Test(); 
     var result = test.Posts.Foo.Bar(123, "abc"); 
     Console.WriteLine(result); 
    } 
} 
public class Test : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, 
     out object result) 
    { 
     result = new MemberAccessWrapper("member accessed was " + binder.Name); 
     return true; 
    } 
    private class MemberAccessWrapper : DynamicObject 
    { 
     private readonly string message; 
     public override bool TryInvoke(InvokeBinder binder, object[] args, 
      out object result) 
     { 
      StringBuilder builder = new StringBuilder(message).Append("("); 
      for(int i = 0 ; i < args.Length ; i++) { 
       if(i!=0)builder.Append(", "); 
       if (args[i] == null) { 
        builder.Append("null"); 
       } else if (args[i] is string) { 
        builder.Append("@\"").Append(((string)args[i]) 
         .Replace("\"", "\"\"")).Append("\""); 
       } else { 
        builder.Append(args[i]); 
       } 
      } 
      builder.Append(")"); 
      result = new MemberAccessWrapper(builder.ToString()); 
      return true; 
     } 
     public MemberAccessWrapper(string message) 
     { 
      this.message = message; 
     } 
     public override string ToString() 
     { 
      return message; 
     } 
     public override bool TryGetMember(GetMemberBinder binder, 
      out object result) 
     { 
      result = new MemberAccessWrapper(message + "." + binder.Name); 
      return true; 
     } 
    } 
} 
+0

Impresionante, estaba editando mi publicación mientras usted publicaba la suya. ¡Gracias! – Phill

1

Quizás podría hacerlo de esta manera implementando las interfaces dinámicas básicas en lugar de 'DynamicObject', pero la forma más simple es que su invocación dinámica de 'Posts' simplemente devuelva otro DynamicObject que manejará el método 'Load'.

Cuestiones relacionadas