当函数将动态对象作为输入参数时,不会检查 C# 类型转换

C# type casting isn't checked when a function takes a dynamic object as input parameter

提问人:Stanton 提问时间:8/25/2023 最后编辑:Stanton 更新时间:8/25/2023 访问量:53

问:

我有一个接受类型对象的函数。该函数确实可以解析该对象,然后返回有关其解析内容的一些信息。出于某种奇怪的原因,当我获取返回的对象并将其存储到另一个变量中时,该新变量不会在编译时检查对象类型是否匹配。dynamic

在下面的示例中,我尽可能地减少了这个问题。

看这里我调用了两个函数,它们都返回一个类型的值,这些函数之间的唯一区别是其中一个采用类型的输入参数。ReturnDouble()doubledynamic

在函数中,我只是调用这两个函数并将它们的值存储到类型变量中。我本来以为这两个调用都会给出编译错误,因为您不能隐式地将 a 转换为 ,但奇怪的是,该函数不会抛出错误。DoSomething()intdoubleintReturnDouble(dynamic obj)

enter image description here

在此示例中,我使用 's 和 's 来保持简单,但在我更大的代码中,我获取该对象并从中解析大量信息,并传回一个更大的类对象。doubleintdynamic obj

public static double ReturnDouble()
{
    return 1.0;
}
public static double ReturnDouble(dynamic obj)
{
    // do some stuff with object 'obj'
    return 1.0;
}
public static void DoSomething()
{
    dynamic obj = new { };
    int forSomeReasonThisCompilesWithoutError = ReturnDouble(obj);
    int asExpectedThisCompilesWithErrors = ReturnDouble();
}
C# 强制转换

评论

0赞 Ralf 8/25/2023
不明白。这些方法返回一个 double,而你附加到的变量是一个 int。你放入方法中的参数是动态的,对这个问题无关紧要?
1赞 Ralf 8/25/2023
请不要把代码作为图片。把它作为文本放在这里,然后人们就有机会复制它并尝试自己。
0赞 Stanton 8/25/2023
这就是重点......我试图找出为什么添加使代码编译良好dynamic obj
0赞 Ralf 8/25/2023
啊,好吧。如果我能够复制并尝试自己;),我会很容易地发现这一点
0赞 Panagiotis Kanavos 8/25/2023
@Stanton您的目标是哪个运行时?在 .NET Core 中,即使 IntelliSence 未显示错误,这两行也会导致编译错误。在 .NET Framework 4.7 中,没有编译错误,但在运行时执行失败

答:

3赞 Sweeper 8/25/2023 #1

obj是一个动态表达式,因为它的类型为 。dynamic

不会对动态表达式进行编译时(静态)绑定。因此,将在运行时完成重载解析。这意味着编译器不知道将返回什么类型。在这种情况下,您可以考虑返回。ReturnDoubleReturnDoubleReturnDoubledynamic

因此,运算符也不是静态绑定的,因此编译器不会检查其两端的表达式是否具有正确的类型。=

相反,动态绑定是在运行时为它们完成的。这意味着您的代码将在运行时引发异常,而不是发出编译器错误。int forSomeReasonThisCompilesWithoutError = ReturnDouble(obj);

规格

...但是,如果表达式是动态表达式(即具有 类型),则表示它参与的任何绑定都应基于其运行时类型,而不是编译时具有的类型。因此,此类操作的绑定将推迟到程序运行期间执行该操作的时间。这称为动态绑定。dynamic

评论

0赞 Ralf 8/25/2023
这是出乎意料的。
0赞 Stanton 8/25/2023
哎哟。。。多么糟糕的设计......我知道函数“内部”的所有内容都可以被认为是动态的,但是他们还将返回类型视为动态这一事实令人震惊。
0赞 Joel Coehoorn 8/25/2023
@Ralf 它有记录。
0赞 Joel Coehoorn 8/25/2023
@Stanton 这就是任何动态语言的工作方式......PHP、JavaScript 等事实上,它更好,因为至少你可以声明预期的类型。不喜欢它?类型安全设计。
0赞 Stanton 8/25/2023
当我做我的 Javascript 工作时,出于类型安全原因,我在 Typescript 中完成。然后,我将类型词传递到参数中,但从函数中传回一个对象。Typescript 可以毫不费力地处理这个问题,并给我提供我在 C 中编译的编译错误#anystrong typed
1赞 Michael Navara 8/25/2023 #2

我不确定将参数定义为 是否实用。相反,我使用 and,当它有用时,我在方法中创建新变量作为动态变量。dynamicobject

public static double ReturnDouble()
{
    return 1.0;
}
public static double ReturnDouble(object obj)
{
    dynamic dynamicObj = obj;
    // do some stuff with object 'dynamicObj'
    return 1.0;
}
public static void DoSomething()
{
    dynamic obj = new { };
    object objAsObject = obj;
    int forSomeReasonThisCompilesWithoutError = ReturnDouble(objAsObject);
    int asExpectedThisCompilesWithErrors = ReturnDouble();
}

评论

0赞 Stanton 8/25/2023
我接受了@sweeper的回答,因为它解释了为什么我没有看到任何编译错误,但这个答案实际上解决了我的问题。事实证明,我不需要使用该类型进行任何操作,只需使用该类型即可。这修复了我的应用程序,因此它现在按预期给了我编译错误。dynamicobject
0赞 Panagiotis Kanavos 8/25/2023 #3

清扫工的回答解释了发生了什么。解决实际问题的更好方法是使用具有类型约束的泛型,而不是使用 擦除类型信息。dynamic

public static T ReturnNotOnlyDouble<T>(T obj,T other) 
    where T:IAdditionOperators<T,T,T>
{
    var result=obj+other;
    return result;
}

此示例有点做作,以显示 .NET 7 如何抽象静态方法(如 IAdditionOperators<> 允许在泛型函数中使用运算符和其他静态方法,这在以前的运行时中是不可能的。我怀疑这就是使用而不是通用方法的原因。dynamic

System.Numerics 现在包含多个运算符接口,包括组合多个运算符的接口,例如 IFloatingPoint,它结合了所有浮点数通用的接口。double 例如实现IFloatingPoint<double>