语法替代动态对象的铸造(Syntax alternatives to casting of dyna

2019-09-16 15:59发布

我有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>

这可以做的工作。 但没有,因此这个问题。

Answer 1:

为了获得智能,你将不得不投的东西是不是值dynamic在一些点。 如果你发现自己这个做了很多,你可以使用辅助方法,在一定程度上缓解疼痛:

GetFoo(dictionary.Foo).SomeMethod();

但是,这并不多过你有什么已经有所改善的。 唯一的其他方式来获得智能感知会投值回非动态类型或避免dynamic摆在首位。

如果你想使用智能感知,它通常是最好避免使用dynamic摆在首位。

typedDictionary["foo"].SomeMethod();

你的榜样使它看起来可能是你对自己的结构具体期望dynamic对象。 考虑是否有创建一个静态类结构,将满足您的需求的方式。

更新

在回答您的更新:如果你不想彻底改变你的语法,我建议使用一个索引,使你的语法可以是这样的:

UI.Map<Foo>().Action<int, object>(x => x.SomeMethodWithParameters)["index"].Validate((val) => {...});

这里是我的推理:

  1. 你只比动态方法添加四个字符(减一)。
  2. 让我们面对现实吧:你使用的是“神奇的字符串。” 通过要求的实际字符串,这个事实将立即向程序员谁看这个代码明显。 采用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>()));

不同于当前的解决方案:

  1. 这不会打破,如果你最终改变方法签名的参数名:就像编译器,该框架将更加注重位置和类型的参数,而不是他们的名字。
  2. 代码运行时的方法签名的任何更改将导致从编译器的直接标志,而不是运行时异常。

这可能稍微容易实现(因为它不需要解析表达式树)的另一种语法可能是:

UI.Map<Foo>().Action((x, v) => x.SomeMethodWithParameters)
    .Validate(v => new{
        index = v.ByMethod<int>(i => {return i > 1;}),
        target = v.IsNotNull()});

这不会给你上面列出的优点,但它仍然给你键入安全(因此智能感知)。 挑选你的毒药。



Answer 2:

除了显式演员

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


Answer 3:

正如前面已经说过(在问题和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变量的潜在成员是不合理的(如果不是不可能的)。



文章来源: Syntax alternatives to casting of dynamic objects