是否可以在动态/ ExpandoObject上创建一个泛型方法?
是否可以在动态/ ExpandoObject上创建一个泛型方法?
我怀疑这是不可能的,但我没有看到明确的否定。\n我目前(正在工作中)的实现如下:\n
public static Main(param args[]) { dynamic Repository = GetRepository(); var query = (Repository.QueryUser() as IQueryable) .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 (); return repo; }
\n为了更好地模拟(常用的)带有泛型方法的Repository接口,我想实现类似以下的内容:\n
public interface IRepository { IQueryableQuery (); } public static Main(param args[]) { IRepository Repository = GetRepository(); // 返回一个dynamic var query = Repository.Query () .Where(user => user.Name.ToLower().Contains("jack")); } public static dynamic GetRepository() { dynamic repo = new System.Dynamic.ExpandoObject(); // 问题出在下面的代码行 repo.Query = new [] { new User() { Name = "Jack Sparrow" } }.AsQueryable (); return repo; }
\n是否有可能绕过这个问题(也许使用System.Dynamic命名空间中的某些东西)?
问题的原因是,动态对象(dynamic / ExpandoObject)的实现不满足接口的要求,它没有一个可以作为`Query
解决方法是使用非泛型方法,如`QueryUsers`、`QueryFoos`等,或者使用指定了`IQueryable
using ImpromptuInterface; using ImpromptuInterface.Dynamic; using System.Linq; public interface IRepository{ IQueryable Query(); } public class User { public string Name { get; set; } } public class Program { public static void Main(params string[] args) { IRepository repo = GetUserRepository(); // dynamically construct user repository var query = repo.Query() .Where(user => user.Name.ToLower().Contains("jack")); } public static IRepository GetUserRepository() { var repo = new { Query = Return >.Arguments(() => new[] { new User() { Name = "Jack Sparrow" } }.AsQueryable()) }.ActLike >(); return repo; } }
使用这种方法,具体的存储库只需为每个实际上是模型类型的`T`实现一个`IRepository
一个非常有趣的建议和方法。
有人在stackoverflow上提出了一个问题:“在动态/ExpandoObject上创建一个通用方法是否可能?”问题的原因是动态类型和ExpandoObject在C#中的使用限制。在C#中,ExpandoObject只能获取或设置属性(或事件),而不能定义新方法。要实现这个功能,需要创建自己的动态对象,并为其添加自己的成员。
然而,有人提出使用组合来解决这个问题。创建一个类,并在其中添加自定义的方法,然后将ExpandoObject作为属性添加进来。这样就可以通过调用该类的方法来实现动态行为。
如果非常希望实现这个功能,可以创建一个带有动态元对象包装器的动态对象,并将其封装在ExpandoObject中。在包装器中,将所有绑定转发到自定义的成员上,其他的绑定则转发到ExpandoObject上。这样,该对象既具有自定义成员的功能,又具有ExpandoObject的特性。
以下是一个示例代码:
// 确保按需显式实现IDictionary// 如果需要,将所有调用转发到ExpandoObject上 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; } // 添加的新方法 public string GetMessage() => "GOT IT!"; public string GetTypeName () => typeof(T).Name; // 确保实现方法以组合结果(如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(); } }
使用这个类,就可以像使用任何ExpandoObject一样使用它。
dynamic expando = new ExtendedExpandoObject(); Console.WriteLine(expando.Value); // 原始的ExpandoObject expando.Length = 12; // 在ExpandoObject上设置一个新属性 Console.WriteLine(expando.Length); // 获取ExpandoObject上的属性 Console.WriteLine(expando.GetMessage()); // 调用新方法 Console.WriteLine(expando.GetTypeName()); // 调用通用方法 Console.WriteLine(expando.Value.Length); // 获取原始ExpandoObject上的属性
需要注意的是,还需要添加一个DynamicMetaObjectWrapper类。这个类是DynamicMetaObject的抽象子类,用于将所有的绑定转发到基本的DynamicMetaObject对象上。
虽然某些情况下在ExpandoObject上添加委托属性来模拟方法,但这并不是真正的方法。委托只是对方法的引用,它无法参与方法重载等功能。
最后,还某些情况下在ExpandoObject上添加委托属性的文档链接。但是需要注意,这只是将委托存储在属性上,而不是真正的方法。委托只是对方法的引用,无法与真正的方法相提并论。
尽管ExpandoObject有一些限制,但还是可以通过创建自定义的动态对象来实现类似的功能。这样就可以在动态对象上定义新的方法。