简体   繁体   English

是否可以在动态/ ExpandoObject上创建通用方法

[英]Is it possible to create a Generic Method on an dynamic / ExpandoObject

I suspect this isn't possible, but I haven't seen a definitive no. 我怀疑这是不可能的,但我还没有看到明确的否定。

My current (working) implementation is as followed.. 我目前的(工作)实施如下:

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;
}

To better mock the (commonly used) Repository interface with Generic methods, I would like to implement something like the following: 为了更好地使用Generic方法模拟(常用的)Repository接口,我想实现类似以下内容:

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;
}

Is there a possible workaround for this (using something in the System.Dynamic namespace perhaps)? 是否有可能的解决方法(使用System.Dynamic命名空间中的某些东西)?

I doubt it, you can only get or set "properties" (or events ) of an ExpandoObject , not define new methods. 我对此表示怀疑,您只能获取或设置ExpandoObject “属性”(或事件 ),而不是定义新方法。 If you wanted to do so, you'll have to create your own dynamic object for which you add your own members. 如果您想这样做,则必须创建自己的动态对象,并为其添加自己的成员。

But I feel I have to say this, why not just use composition instead? 但是我觉得我必须这样说,为什么不用组合代替呢? Create a class to which you add your methods, and have a property for the expando. 创建一个添加方法的类,并为expando创建一个属性。

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

If you absolutely wanted to do this, you could create a dynamic object with a dynamic meta object wrapper over an ExpandoObject . 如果您绝对想要这样做,可以在ExpandoObject创建一个动态元对象包装器的动态对象。 Then in the wrapper, forward all bindings to your members to your dynamic object, and all others to the expando. 然后在包装器中,将所有绑定转发给您的动态对象,将所有其他绑定转发给expando。 The object would be what you define it, with the functionality of an expando. 该对象将是您定义它的对象,具有expando的功能。

eg, 例如,

// 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();
    }
}

With this, you could use it like you would with any expando object. 有了这个,你可以像使用任何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

You'll just need the DynamicMetaObjectWrapper: 你只需要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();
}

Your hypothetical expando object implementation doesn't "satisfy" the interface; 您假设的expando对象实现不“满足”接口; it doesn't have a generic Query<T> method that can be called as Query<Foo>() or Query<Bar>() . 它没有可以作为Query<Foo>()Query<Bar>()调用的通用Query<T>方法。 It only has a non generic Query() method that will return IQueryable<User> . 它只有一个非通用的Query()方法,它将返回IQueryable<User> That said, it doesn't seem like Query<T> is a reasonable thing to put in your interface anyway, especially without type constraints. 也就是说,无论如何, Query<T>似乎都是合理的,特别是没有类型约束。

I would suggest using either using non generic methods like QueryUsers , QueryFoos etc., or having an IRepository<T> interface that specifies a IQueryable<T> Query() method. 我建议使用非泛型方法,如QueryUsersQueryFoos等,或者使用指定IQueryable<T> Query()方法的IRepository<T>接口。 Here is how you could use the Impromptu-Interface library to implement the latter approach: 以下是如何使用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;
    }
}

Using this approach, your concrete repository would simply implement an IRepository<T> interface for each T that is actually a model type. 使用这种方法,您的具体资料库只想实现一个IRepository<T>为每个接口T ,其实是一个型号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 是否可以使用ExpandoObject创建动态树结构? - Is it possible to create a Dynamic tree structure using the ExpandoObject? 当属性名称是动态的时使用 ExpandoObject,这可能吗? - Using ExpandoObject when property names are dynamic, is this possible? 如何创建具有动态属性的类(Expandoobject?) - How to create a class with dynamic property (Expandoobject?) 是否可以创建一个通用的按位枚举&#39;IsOptionSet()&#39;方法? - Is it possible to create a generic bitwise enumeration 'IsOptionSet()' method? 动态/ ExpandoObject方法抛出“找不到类型为“动作”的默认成员” - dynamic/ExpandoObject method throws 'No default member found for type 'Action' 为什么推导的带有ExpandoObject参数的方法的返回类型总是动态的? - Why the deduced return type of a method with a ExpandoObject parameter is always dynamic? 是否可以使用 ExpandoObject 创建运行时属性? - Is it possible to use ExpandoObject to create run-time properties? 有什么方法可以创建具有与dynamic或ExpandoObject相同行为的类? - There is any way to create a class with the same behavior as dynamic or ExpandoObject? 是否可以在泛型方法的主体内创建泛型类型的新实例? - Is it possible create a new instance of a generic type inside the body of a generic method? JavaScriptConverter,ExpandoObject和动态类型 - JavaScriptConverter, ExpandoObject and dynamic types
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM