2

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

这可以发挥作用。但没有,因此问题。

4

3 回答 3

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) => {...});

这是我的推理:

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

这不会给你上面列出的优势,但它仍然给你类型安全(因此是智能感知)。选择你的毒药。

于 2012-04-21T04:51:18.970 回答
2

除了显式演员

((FooClass)dictionary.foo).SomeMethod();

安全铸造

(dictionary.foo as FooClass).SomeMethod();

切换回静态调用(这将允许智能感知工作)的唯一其他方法是执行Implicit Cast

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(); 
于 2012-04-24T17:55:24.407 回答
0

正如已经说过的(在问题和StriplingWarrior答案中)C# 4dynamic类型不提供智能感知支持。提供此答案仅是为了解释原因(根据我的理解)。

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可能是 anInt32 a ,String具体取决于代码运行的时间。我知道,这是一个极端的例子,尽管它清楚地说明了问题。

考虑到单个dynamic变量在运行时可能代表任意数量的类型,编译器或 IDE 几乎不可能在其执行之前对其所代表的类型做出假设,因此dynamic变量的设计或编译时解析潜在成员是不合理的(如果不是不可能的话)。

于 2012-04-21T05:16:02.047 回答