我有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的智能感知工作:
dictionary.foo.SomeMethod() <--would like SomeMethod to pop up in intellisense
我发现这样做的唯一方法是:
((FooClass)dictionary.foo).SomeMethod()
谁能推荐一个更优雅的语法? 我很舒服写DynamicDictionary与IDynamicMetaObjectProvider自定义实现。
更新:
有人问为什么动力学,什么我的具体问题。 我有一个系统,可以让我做这样的事情:
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>
这可以做的工作。 但没有,因此这个问题。
为了获得智能,你将不得不投的东西是不是值dynamic
在一些点。 如果你发现自己这个做了很多,你可以使用辅助方法,在一定程度上缓解疼痛:
GetFoo(dictionary.Foo).SomeMethod();
但是,这并不多过你有什么已经有所改善的。 唯一的其他方式来获得智能感知会投值回非动态类型或避免dynamic
摆在首位。
如果你想使用智能感知,它通常是最好避免使用dynamic
摆在首位。
typedDictionary["foo"].SomeMethod();
你的榜样使它看起来可能是你对自己的结构具体期望dynamic
对象。 考虑是否有创建一个静态类结构,将满足您的需求的方式。
更新
在回答您的更新:如果你不想彻底改变你的语法,我建议使用一个索引,使你的语法可以是这样的:
UI.Map<Foo>().Action<int, object>(x => x.SomeMethodWithParameters)["index"].Validate((val) => {...});
这里是我的推理:
- 你只比动态方法添加四个字符(减一)。
- 让我们面对现实吧:你使用的是“神奇的字符串。” 通过要求的实际字符串,这个事实将立即向程序员谁看这个代码明显。 采用
dynamic
方法,没有什么表示,“指数”是不是从编译器的角度来看,一个已知值。
如果你愿意改变周围的相当多的东西,你可能要进行调查的方式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()});
这不会给你上面列出的优点,但它仍然给你键入安全(因此智能感知)。 挑选你的毒药。
除了显式演员 ,
((FooClass)dictionary.foo).SomeMethod();
或安全角色 ,
(dictionary.foo as FooClass).SomeMethod();
唯一的其他方式切换回静态调用(这将使智能感知工作)是做隐式转换 :
FooClass foo = dictionary.foo;
foo.SomeMethod().
声明铸造是你唯一的选择,不能使用辅助方法,因为它们将被动态调用给你同样的问题。
更新:
不知道这是更优雅,但不涉及铸造一堆并得到拉姆达外智能感知:
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();
正如前面已经说过(在问题和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
在后者。 使用的+=
操作实际上是两个不同的调用它之间的不同,因为涉及的类型是在运行时推断(意为编译器不理解或推断在编译时间类型的使用)。
现在考虑一个微小的变化:
dynamic v;
if (DateTime.Now.Second % 2 == 0)
v = 0;
else
v = "Hello";
v += 1;
Console.WriteLine("{0}", v);
在这个例子中, v
可能潜在地任一种Int32
或 String
根据在该代码运行的时间。 一个极端的例子,我知道,但它清楚地说明了这个问题。
考虑一个单一的dynamic
变量可能代表任何数量的类型在运行时,这将是编译器或IDE做出关于它代表之前,它的类型的假设几乎是不可能的执行,所以设计-或编译时的分辨率dynamic
变量的潜在成员是不合理的(如果不是不可能的)。