我有一个DynamicDictionary的实现,其中字典中的所有条目都是已知类型:
public class FooClass
{
public void SomeMethod()
{
}
}
dynamic dictionary = new DynamicDictionary<FooClass>();
dictionary.foo = new FooClass();
dictionary.foo2 = new FooClass();
dictionary.foo3 = DateTime.Now; <--throws exception since DateTime is not FooClass
我想要的是能够在引用其中一个字典条目的方法时使Visual Studio Intellisense工作:
dictionary.foo.SomeMethod() <--would like SomeMethod to pop up in intellisense
我发现这样做的唯一方法是:
((FooClass)dictionary.foo).SomeMethod()
有人可以推荐更优雅的语法吗?我很高兴用IDynamicMetaObjectProvider编写DynamicDictionary的自定义实现。
更新:
有些人问为什么动态以及我的具体问题是什么。我有一个让我做这样的事情的系统:
UI.Map<Foo>().Action<int, object>(x => x.SomeMethodWithParameters).Validate((parameters) =>
{
//do some method validation on the parameters
return true; //return true for now
}).WithMessage("The parameters are not valid");
在这种情况下,方法SomeMethodWithParameters具有签名
public void SomeMethodWithParameters(int index, object target)
{
}
我现在拥有的用于为各个参数注册验证的内容如下所示:
UI.Map<Foo>().Action<int, object>(x => x.SomeMethodWithParameters).GetParameter("index").Validate((val) =>
{
return true; //valid
}).WithMessage("index is not valid");
我希望它是:
UI.Map<Foo>().Action<int, object(x => x.SomeMethodWithParameters).index.Validate((val) =>
{
return true;
}).WithMessage("index is not valid");
这可以使用动态,但是在引用索引后你失去了智能感知 - 现在这很好。问题是是否有一种聪明的语法方法(除了上面提到的方法)让Visual Studio以某种方式识别类型。听起来像答案是“不”。
在我看来,如果有IDynamicMetaObjectProvider的通用版本,
IDynamicMetaObjectProvider<T>
这可以成功。但事实并非如此。
答案 0 :(得分:4)
为了获得智能感知,您必须在某个时刻将某些内容转换为非dynamic
的值。 如果你发现自己做了很多,你可以使用辅助方法来缓解疼痛:
GetFoo(dictionary.Foo).SomeMethod();
但这并不比你已经得到的改进大很多。获得智能感知的唯一方法是将值转换回非动态类型或首先避免使用dynamic
。 击>
如果您想使用Intellisense,通常最好避免首先使用dynamic
。
typedDictionary["foo"].SomeMethod();
您的示例使您可能对dynamic
对象的结构有特定的期望。考虑是否有办法创建一个满足您需求的静态类结构。
响应您的更新:如果您不想彻底更改语法,我建议您使用索引器,以便您的语法如下所示:
UI.Map<Foo>().Action<int, object>(x => x.SomeMethodWithParameters)["index"].Validate((val) => {...});
这是我的理由:
dynamic
方法,没有任何迹象表明&#34;索引&#34;从编译器的角度来看,它不是一个已知的值。如果您愿意更改一些内容,可能需要调查Moq
在语法中使用表达式的方式,尤其是It.IsAny<T>()
方法。看起来你可以在这些方面做更多的事情:
UI.Map<Foo>().Action(
(x, v) => x.SomeMethodWithParameters(
v.Validate<int>(index => {return index > 1;})
.WithMessage("index is not valid"),
v.AlwaysValid<object>()));
与您当前的解决方案不同:
另一种可能稍微容易实现的语法(因为它不需要解析表达式树)可能是:
UI.Map<Foo>().Action((x, v) => x.SomeMethodWithParameters)
.Validate(v => new{
index = v.ByMethod<int>(i => {return i > 1;}),
target = v.IsNotNull()});
这并没有为您提供上面列出的优势,但它仍然为您提供了类型安全性(因此也提供了智能感知)。选择你的毒药。
答案 1 :(得分:2)
除了 Explict Cast ,
((FooClass)dictionary.foo).SomeMethod();
或安全投射,
(dictionary.foo as FooClass).SomeMethod();
切换回静态调用(允许intellisense工作)的唯一方法是执行隐式投射:
FooClass foo = dictionary.foo;
foo.SomeMethod().
声明的转换是你唯一的选择,不能使用辅助方法,因为它们会被动态调用,给你同样的问题。
的更新强> 的
不确定这是否更优雅,但不涉及施放一堆并在lambda之外获取智能感知:
public class DynamicDictionary<T>:IDynamicMetaObjectProvider{
...
public T Get(Func<dynamic,dynamic> arg){
return arg(this);
}
public void Set(Action<dynamic> arg){
arg(this);
}
}
...
var dictionary = new DynamicDictionary<FooClass>();
dictionary.Set(d=>d.Foo = new FooClass());
dictionary.Get(d=>d.Foo).SomeMethod();
答案 2 :(得分:0)
正如已经说过的那样(在问题和StriplingWarrior回答中),C#4 dynamic
类型不提供智能感知支持。提供此答案仅仅是为了解释为什么(基于我的理解)。
dynamic
到C#编译器只不过是object
,它在编译时只知道它支持的成员。区别在于,在运行时,dynamic
尝试根据表示的实例所知的类型来解析针对其实例调用的成员(提供后期绑定的形式)。
请考虑以下事项:
dynamic v = 0;
v += 1;
Console.WriteLine("First: {0}", v);
// ---
v = "Hello";
v += " World";
Console.WriteLine("Second: {0}", v);
在此代码段中,v
代表Int32
的实例(如代码的第一部分所示)和后者的String
实例。 +=
运算符的使用实际上在两个不同的调用之间有所不同,因为涉及的类型是在运行时推断的(意味着编译器在编译时不理解或推断类型的使用)。 / p>
现在考虑一点变化:
dynamic v;
if (DateTime.Now.Second % 2 == 0)
v = 0;
else
v = "Hello";
v += 1;
Console.WriteLine("{0}", v);
在此示例中,v
可能是Int32
或 a String
,具体取决于代码运行的时间。我知道,这是一个极端的例子,尽管它清楚地说明了这个问题。
考虑到单个dynamic
变量在运行时可能代表任意数量的类型,编译器或IDE几乎不可能在它执行之前对它所代表的类型进行假设,因此设计 - 或dynamic
变量潜在成员的编译时解析是不合理的(如果不是不可能的话)。