Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#DLR,使用动态关键字的数据类型推断_C#_.net_Dynamic Language Runtime - Fatal编程技术网

C#DLR,使用动态关键字的数据类型推断

C#DLR,使用动态关键字的数据类型推断,c#,.net,dynamic-language-runtime,C#,.net,Dynamic Language Runtime,只是问: 为什么“withOffset”变量被推断为动态而解析方法返回Struct dynamic str = "22/11/2013 10:31:45 +00:01"; var withOffset = DateTimeOffset.Parse(str); 在显式地将其转换回Struct之后 dynamic str = "22/11/2013 10:31:45 +00:01"; var withOffset = DateTimeOffset.Parse((string)str); 因为Da

只是问:

为什么“withOffset”变量被推断为动态解析方法返回Struct

dynamic str = "22/11/2013 10:31:45 +00:01";
var withOffset = DateTimeOffset.Parse(str);
在显式地将其转换回Struct之后

dynamic str = "22/11/2013 10:31:45 +00:01";
var withOffset = DateTimeOffset.Parse((string)str);
因为DateTimeOffset.Parse的返回类型是DateTimeOffset,编译器必须知道这一点。记住,不管它在运行时调用什么方法,返回的总是DateTimeOffset

规格说明

由于您的方法将dynamic作为参数,因此它符合“动态绑定”的条件

有点可疑

有这样的规范有什么意义?或者在哪种情况下,DateTimeOffset.Parse不会返回STRUCT(暂时忘记DLR..)


如果类中的所有方法/重载都具有相同的返回类型,则编译器需要更聪明,以便在长期内获得性能优势。

当您使用
动态时,整个表达式在编译时被视为动态表达式,这会使编译器将所有内容都视为动态的,并获得运行时绑定

C语言规范第7.2节对此进行了解释:

当不涉及动态表达式时,C#默认为静态绑定,这意味着在选择过程中使用组成表达式的编译时类型。但是,当上面列出的操作中的一个组成表达式是动态表达式时,该操作将被动态绑定

这基本上意味着大多数操作(类型在规范第7.2节中列出)如果有任何元素被声明为
动态
,将被评估为
动态
,结果将是
动态


这是因为在下线str是动态的

         dynamic str = "22/11/2013 10:31:45 +00:01";
        var withOffset = DateTimeOffset.Parse(str);
在编译时str是动态的,只有在运行时才能知道str的类型,这就是编译器将withOffset视为动态的原因



您知道str被转换为datetime结构,但对于编译器来说,它只在运行时才知道…

这里有一个非常好的示例,说明您对返回类型的假设开始出错

public class Bar
{
    public string Foo(string value)
    {
        return value;
    }
}
正如您在这里看到的,
Bar
显然有一个实例方法
Foo
,它接受一个字符串并返回一个字符串

public class Baz : Bar, IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression parameter)
    {
        return new BazMetaObject(parameter, this);
    }
}
但是现在我创建了一个派生类,它也实现了。这是C#用来获取a的接口,它决定了在运行时如何绑定动态调用。例如:

public class BazMetaObject : DynamicMetaObject
{
    public BazMetaObject(Expression expression, Baz value)
        : base(expression, BindingRestrictions.Empty, value)
    {
    }

    public override DynamicMetaObject BindInvokeMember(
        InvokeMemberBinder binder, DynamicMetaObject[] args)
    {
        if (binder.Name == "Foo")
        {
            return new DynamicMetaObject(
                Expression.Convert(
                    Expression.Call(
                        typeof (BazMetaObject).GetMethod("DynamicFoo")
                    ),
                    typeof (object)
                ),
                BindingRestrictions.GetTypeRestriction(
                    this.Expression,
                    this.LimitType
                )
            );
        }

        return base.BindInvokeMember(binder, args);
    }

    public static int DynamicFoo()
    {
        return 1234;
    }
}
DynamicMetaObject
重载将捕获对
Foo
的任何调用,并将它们动态重定向到
DynamicFoo
,后者具有完全不同的签名,包括返回
int
,而不是
string

所以,如果你要这么做

dynamic value = "Hello, world!";

Bar bar = new Baz();
var returnValue = bar.Foo(value);
…运行时
returnValue
的值将是
1234
,而不是
“你好,世界!”

现在,在现实世界中,这是疯狂的邪恶。虽然可以完全重新绑定期望以某种方式执行某些操作的函数,但这样做只会让人感到困惑。这就是说,在CLR中这是完全可能的


TL;DR:当你使用
动态
时,你不能总是确定你认为正确的事情。

不,我们已经知道DateTimeOffset.Parse总是返回STRUCT(DateTimeOffset),那么为什么var会推断为动态的呢?因为正如我所说的,str是动态的,var会被转换成与之相关的数据类型,我想说的是,在这种情况下,var将是动态的,因为我们事先知道返回类型是Struct,有这样粗鲁的规范有什么意义?或者DateTimeOffset.Parse是否会返回“Dynamic”?@DipakBava:由于参数是
Dynamic
,编译器无法知道在运行时调用哪个方法。因此,它无法知道返回类型是什么。我们可能知道返回类型将是
DateTimeOffset
,但编译器不知道,也不可能知道。@Richard,您有符合规范的点,这是否意味着如果类“Bar”中只有一个方法“public int Foo(int i)”,还有,为什么编译器需要返回动态,因为Foo没有其他重载,而且类型“Bar”是众所周知的。您可以扩展“推断为动态”吗?例如,你能添加“withOffset.foo=4;”并得到一个好的编译吗?“推断为动态”编译器认为DLR的工作就是处理这个问题……所以在你的第一个例子中,如果你添加了另一行“withOffset.foo=4;”,编译器不会标记这个,你只会在运行时得到一个错误。因为这是C语言规范所说的。第7.6.5章因为预测运行时实际绑定的方法是危险的。很抱歉,您可以使用BindInvokeMember拦截调用。然而,我觉得在继承链方面,OOP的概念是错误的。为什么基类/接口需要知道其方法/属性的派生类实现,如果基类必须知道(很少),那么方法/属性是抽象的/虚拟的,因此,方法签名保持不变。因此,在本例中,bar.Foo(value)表示意图是使用签名字符串Foo(动态值)调用Foo方法。也就是说,Baz是从Bar派生的,这意味着Bar对Baz一无所知,除非Bar将Foo()设为虚拟(但返回类型保持不变) ). 除了运行时动态解析的任何类型之外,方法的返回类型都将保持不变,除非在同一个/基类中存在具有不同返回类型的方法组