2015-07-12 46 views
7

我怀疑这是不可能的,但我还没有看到明确的no。是否可以在动态/ ExpandoObject上创建通用方法

我现在的(工作)的实现是遵循..

public static Main(param args[]) 
{ 
    dynamic Repository = GetRepository(); 

    var query = (Repository.QueryUser() as IQueryable<User>) 
       .Where(user => user.Name.ToLower().Contains("jack")); 
} 

public static dynamic GetRepository() 
{ 
    dynamic repo = new System.Dynamic.ExpandoObject();   
    repo.QueryUser = new [] { new User() { Name = "Jack Sparrow"}}.AsQueryable<User>(); 
    return repo; 
} 

为了更好地嘲笑与通用方法(常用)库接口,我想实现类似如下:

public interface IRepository 
{ 
    IQueryable<T> Query<T>(); 
} 

public static Main(param args[]) 
{ 
    IRepository Repository = GetRepository(); // return a dynamic 

    var query = Repository.Query<User>() 
       .Where(user => user.Name.ToLower().Contains("jack")); 
} 


public static dynamic GetRepository() 
{ 
    dynamic repo = new System.Dynamic.ExpandoObject(); 

    // the issue is on the following line 
    repo.Query<User> = new [] { 
          new User() { 
           Name = "Jack Sparrow" 
          } 
         }.AsQueryable<User>(); 
    return repo; 
} 

是否有可能的解决方法(使用System.Dynamic名称空间中的某些内容)?

+0

你可以创建一个具有“查询”方法的类吗? – usr

+0

我会怀疑这是不可能的,因为模板被编译为独特的类和调用。然而,通过发射IL代码可能是可能的。 – Rob

+0

http:// stackoverflow。com/questions/3712732/how-to-create-a-generic-list-with-a-dynamic-object-type?rq = 1 ...嗯......这可能证明是相对的 –

回答

4

我对此表示怀疑,你只能得到或设置ExpandoObject的“属性”(或events),而不能定义新的方法。如果你想这样做,你必须创建自己的动态对象,为其添加自己的成员。

但我觉得我必须这样说,为什么不直接用组合物呢?创建一个您添加方法的类,并为expando创建一个属性。

class MyClass 
{ 
    public dynamic Expando { get; } = new ExpandoObject(); 
    public void MyMethod<T>() { } 
} 

如果你绝对要做到这一点,你可以创建一个动态的元对象封装在一个ExpandoObject动态对象。然后在包装器中,将所有绑定转发给您的成员到动态对象,并将所有其他绑定到expando。这个对象就是你定义它的地方,以及一个expando的功能。

例如,

// be sure to explicitly implement IDictionary<string, object> 
// if needed forwarding all calls to the expando 
class ExtendedExpandoObject : IDynamicMetaObjectProvider 
{ 
    DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) => new MyMetaObject(parameter, this); 

    public ExtendedExpandoObject(ExpandoObject expandoObject = null) 
    { 
     Value = expandoObject ?? new ExpandoObject(); 
    } 
    public ExpandoObject Value { get; } 

    // the new methods 
    public string GetMessage() => "GOT IT!"; 
    public string GetTypeName<T>() => typeof(T).Name; 

    // be sure to implement methods to combine results (e.g., GetDynamicMemberNames()) 
    class MyMetaObject : DynamicMetaObjectWrapper 
    { 
     public MyMetaObject(Expression parameter, ExtendedExpandoObject value) 
       : base(new DynamicMetaObject(parameter, BindingRestrictions.Empty, value)) 
     { 
      var valueParameter = Expression.Property(
       Expression.Convert(parameter, typeof(ExtendedExpandoObject)), 
       "Value" 
      ); 
      IDynamicMetaObjectProvider provider = value.Value; 
      ValueMetaObject = provider.GetMetaObject(valueParameter); 
     } 
     protected DynamicMetaObject ValueMetaObject { get; } 

     public override DynamicMetaObject BindGetMember(GetMemberBinder binder) 
     { 
      if (IsMember(binder.Name)) 
       return base.BindGetMember(binder); 
      return ValueMetaObject.BindGetMember(binder); 
     } 

     public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) 
     { 
      if (IsMember(binder.Name)) 
       return base.BindSetMember(binder, value); 
      return ValueMetaObject.BindSetMember(binder, value); 
     } 

     private bool IsMember(string name) => typeof(ExtendedExpandoObject).GetMember(name).Any(); 
    } 
} 

有了这个,就像你使用任何的expando对象,您可以使用它。

dynamic expando = new ExtendedExpandoObject(); 
Console.WriteLine(expando.Value);   // the original expando 
expando.Length = 12;      // set a new property on the expando 
Console.WriteLine(expando.Length);   // get a property on the expando 
Console.WriteLine(expando.GetMessage()); // call the new method 
Console.WriteLine(expando.GetTypeName<ExtendedExpandoObject>()); // call the generic method 
Console.WriteLine(expando.Value.Length); // get the property on the original expando 

你只需要DynamicMetaObjectWrapper:

public abstract class DynamicMetaObjectWrapper : DynamicMetaObject 
{ 
    protected DynamicMetaObjectWrapper(DynamicMetaObject metaObject) 
      : base(metaObject.Expression, metaObject.Restrictions, metaObject.Value) 
    { 
     BaseMetaObject = metaObject; 
    } 
    public DynamicMetaObject BaseMetaObject { get; } 

    public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) => BaseMetaObject.BindBinaryOperation(binder, arg); 
    public override DynamicMetaObject BindConvert(ConvertBinder binder) => BaseMetaObject.BindConvert(binder); 
    public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) => BaseMetaObject.BindCreateInstance(binder, args); 
    public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) => BaseMetaObject.BindDeleteIndex(binder, indexes); 
    public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) => BaseMetaObject.BindDeleteMember(binder); 
    public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) => BaseMetaObject.BindGetIndex(binder, indexes); 
    public override DynamicMetaObject BindGetMember(GetMemberBinder binder) => BaseMetaObject.BindGetMember(binder); 
    public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) => BaseMetaObject.BindInvoke(binder, args); 
    public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) => BaseMetaObject.BindInvokeMember(binder, args); 
    public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) => BaseMetaObject.BindSetIndex(binder, indexes, value); 
    public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) => BaseMetaObject.BindSetMember(binder, value); 
    public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) => BaseMetaObject.BindUnaryOperation(binder); 
    public override IEnumerable<string> GetDynamicMemberNames() => BaseMetaObject.GetDynamicMemberNames(); 
} 
+0

“我怀疑它,你只能得到或设置ExpandoObject的”属性“(或事件),而不是定义新的方法。”这不是真的。 可以在ExpandoObject上添加新方法。检查文档:https://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.110%29.aspx 以下是文档中的示例: 'dynamic expando =新的ExpandoObject();' 'expando.number = 10;' 'expando.Increment =(Action)(()=> {expando.number ++;});' 'Console.WriteLine(expando.number); //打印10' 'expando.Increment();' 'Console.WriteLine(expando.number); // Prints 11' –

+0

这不是一种方法,它是持有委托的属性。这是不一样的。除其他外,它不能参与方法重载。 –

+0

是的,你是正确的,因为ExpandoObject不是方法的声明类型,它只是持有对属性中方法的引用。这也记录在我提供的链接中。尽管如此,文档仍然将其解释为“添加到ExpandoObject”的一种方法。委托是对方法的引用(在本例中是lambda)。无论它是否存储在动态属性中,都不会降低其成为方法。 –

3

你假想的expando对象实现不 “满足” 的界面;它没有可称为Query<Foo>()Query<Bar>()的通用Query<T>方法。它只有一个非通用的Query()方法,将返回IQueryable<User>。也就是说,无论如何,它似乎并不是Query<T>是一个合理的东西,尤其是没有类型限制的情况下。

我会建议使用非通用方法,如QueryUsersQueryFoos等,或有一个IRepository<T>接口,指定一个IQueryable<T> Query()方法。这里是你如何可以使用Impromptu-Interface库来实现后一种方法:

using ImpromptuInterface; 
using ImpromptuInterface.Dynamic; 
using System.Linq; 

public interface IRepository<T> 
{ 
    IQueryable<T> Query(); 
} 

public class User 
{ 
    public string Name { get; set; } 
} 

public class Program 
{ 
    public static void Main(params string[] args) 
    { 
     IRepository<User> repo = GetUserRepository(); // dynamically construct user repository 

     var query = repo.Query() 
        .Where(user => user.Name.ToLower().Contains("jack")); 
    } 

    public static IRepository<User> GetUserRepository() 
    { 
     var repo = new 
     { 
      Query = Return<IQueryable<User>>.Arguments(() => new[] { 
       new User() { 
        Name = "Jack Sparrow" 
       } 
      }.AsQueryable()) 
     }.ActLike<IRepository<User>>(); 

     return repo; 
    } 
} 

使用这种方法,您的具体资料库只想实现一个IRepository<T>接口各T这实际上是一种模型类型。

+0

一个非常有趣的建议和方法 –

相关问题