51

C# 是否与 VB.NET 的 DirectCast 等效?

我知道它有 () 强制转换和 'as' 关键字,但它们与 CType 和 TryCast 一致。

需要明确的是,这些关键字执行以下操作;

CType/() casts:如果它已经是正确的类型,则进行转换,否则查找类型转换器并调用它。如果未找到类型转换器,则抛出 InvalidCastException。

TryCast/"as" 关键字:如果是正确的类型,则强制转换,否则返回 null。

DirectCast:如果它是正确的类型,则进行强制转换,否则抛出 InvalidCastException。

在我阐明了上述内容之后,仍然有人回应说 () 是等价的,所以我将进一步解释为什么这不是真的。

DirectCast 仅允许在继承树上缩小或扩大转换。它不支持像 () 那样跨不同分支的转换,即:

C# - 编译并运行:

//This code uses a type converter to go across an inheritance tree
double d = 10;
int i = (int)d;

VB.NET - 这不会编译

'Direct cast can only go up or down a branch, never across to a different one.
Dim d As Double = 10
Dim i As Integer = DirectCast(d, Integer)

在 VB.NET 中与我的 C# 代码等效的是 CType:

'This compiles and runs
Dim d As Double = 10
Dim i As Integer = CType(d, Integer)
4

11 回答 11

14

很明显,您想要的功能不在 C# 中。不过试试这个...

static T DirectCast<T>(object o, Type type) where T : class
{
    if (!(type.IsInstanceOfType(o)))
    {
        throw new ArgumentException();
    }
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

或者,即使它与 VB 不同,也可以这样称呼它:

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}
于 2010-04-21T15:31:13.760 回答
8

第二次更新

好的,这是一个 C# 方法,据称它基本上DirectCast可以在 VB.NET 中完成。

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

以下是上述方法的问题:

  1. 它有一个where T : class约束,DirectCast但没有。
  2. 它将它的论点包装为System.Object- 再次,不是真的DirectCast(至少不是我所知道的)。
  3. as不必要地使用(这就是为什么它class首先具有约​​束);InvalidCastException如果它不起作用,调用 (T)o 会抛出一个;为什么使用 来检查值是否匹配as,只是为了抛出与一(T)o开始的路线相同的异常?

该方法实际上可以重写以提供相同的结果,DirectCast如下所示:

static T DirectCast<T>(object o) {
    return (T)o;
}

有趣的观察:实际上,这种方法所做的只是对一个值进行装箱,然后尝试将其拆箱。换句话说,DirectCast<int>(12.0)实际上与(int)(object)12.0(并且两者都会抛出异常)相同。意识到这一点使得所提出的DirectCast<T>方法完全没有必要。

现在,这是一个示例,说明VB.NET 和 C# 之间的“不同”方式DirectCast和强制转换方式:()

VB:

Dim i As Integer = 12
Dim l As Long = DirectCast(i, Long) ' does not compile '

C#:

int i = 12;
long l = i; // DOES compile

好的,所以一个编译,另一个不编译。但是看看那个代码。当你已经知道一个对象的类型时有什么意义?DirectCast这不是一个现实的比较,因为在 VB.NET 中永远没有任何理由DirectCast像上面的代码那样调用。(如果你想在 VB.NET 中将已知类型System.Int32的值转换为类型的值System.Int64,你会使用CLng,而不是DirectCast。)如果有一个类型化的变量System.Object那么使用DirectCast, 和下面的代码确实是等价的:

VB:

Dim i As Integer = 12
Dim o As Object = i
Dim l As Long = DirectCast(o, Long) ' compiles, throws an exception '

C#:

int i = 12;
object o = i;
long l = (long)o; // compiles, throws an exception

因此,我坚持认为,DirectCast在 VB.NET 中,在任何使用它实际上有意义的场景中(即,当编译时不知道对象的类型时),都与C# 中的直接样式转换相同()


编辑:好吧,我因为发布了一些没有编译的 VB 代码而感到羞耻。在重新考虑了我所说的话后,我撤回了第二个答案,但保留了第一个。

如果您指的是DirectCast获取未知类型的对象并尝试将其强制转换为所需类型的用法,那么它C# 的 () 强制转换相同:

VB:

Dim o As Object = SomeObject()
Dim i As Integer = DirectCast(o, Integer)

C#:

object o = SomeObject();
int i = (int)o;

这是因为,如果o键入为 a System.Object,则()C# 中的操作将尝试将其拆箱。如果类型不完全匹配,这将失败;例如, if ois a boxed System.Double,然后(int)o将抛出异常,因为o 必须将其拆箱为 aSystem.Double才能将其转换为 a System.Int32(如果您不相信我,请自己尝试一下!)。


注意:以下是不准确的,因为DirectCast执行扩大转换;无论如何,我要把它留给后代。

另一方面,在处理扩大与缩小转换时,使用()C# 中的操作比简单的转换做更多的工作,正如您所指出的(即,您可以这样做(int)someDouble)。在这种情况下,DirectCast相当于 C# 中的普通旧赋值:

VB:

Dim i As Integer = 12
Dim l As Long = DirectCast(i, Long) ' does not compile, actually '

C#:

int i = 12;
long l = i;
于 2010-04-21T16:06:47.387 回答
1

实际上,如果编译器推断类型化变量无法转换为其他类型,则编译器只会捕获 DirectCast 违规。

这些是实际的等价物:

double d = 10;
int i = (int)d;
Dim d As Double = 10
Dim i As Integer = d

请注意此构造的危险性。当您只是在 VB.NET 中将 double 分配给 integer 时,double 会意外地缩小为 integer。

而 C# 程序员获得了编译时安全性,不会意外缩小 .NET 中的变量大小。VB.NET 程序员不得不一直使用 DirectCast 作为一种安全的编程习惯。

这些是实际的等价物:

// Will not compile, cannot convert double to int

double d = 10;
int i = d;
' Will not compile, cannot convert double to int

Dim d As Double = 10
Dim i As Integer = DirectCast(d, Integer)

关于丹涛的评论

无需在 C# 中使用 DirectCast。运行时还可以防止将 long 加载到整数值。这就是 OP 所争论的,C# 没有 DirectCast,DirectCast 可以防止分配不同类型的变量,而“因为”C# 没有这个 DirectCast,它会在分配不同类型时默默地出错。但正如你所看到的,情况并非如此。C# 的转换与 DirectCast完全相同。这将导致InvalidCastException运行时错误:

long l = 10;
object o = l;
int i = (int)o;

这也会导致与上面相同的运行时错误

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = DirectCast(o, Integer)

现在,这就是“有趣”部分的用武之地。使用 VB.NET,您必须记住许多关键字才能完成某些事情。在 C# 中,如果给定的关键字可以在另一个场景中使用(比如在这个变量的向下转换中),他们不会为了实现它而发明另一个关键字。

在 C# 中,你只需要这样做:

long l = 10;
object o = l;
int i = (int)(long)o;

在 VB.NET 中,如果你真的想向下转换变量,并且想要正交的方式来做到这一点,即只记住一个关键字,你必须这样做:

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = DirectCast(DirectCast(o, Long), Integer)

但这不会编译,那么我们如何实现将 long 向下转换为整数呢?您必须记住 VB.NET 的其他关键字。而在 C# 中,它是正交的,您可以使用此构造拆箱变量(typehere),也可以使用相同的构造向下转换/向上转换(typehere)。在 VB.NET 中,从对象加载值和向下转换之间存在根本的脱节。所以在 VB.NET 中,你必须这样做:

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = CType(o, Integer)

嗯.. 我认为 OP 的困惑源于 C# 多次使用(typehere). 一是用于向下转换;其次,同样的构造(查看这篇文章的第一部分,object o = l)也用于从对象中拆箱值,请放心,它具有 DirectCast 的安全类型转换行为。他们是一样的!

这种垂头丧气...

long l = 1;
int i = (int) l;

...不等于:

Dim l As Long = 1
Dim i As Integer = DirectCast(l, Integer)

如果你想执行向下转换,你必须这样做:

Dim l As Long = 1
Dim i As Integer = CInt(l) ' Can also use CType

现在,如果一个 VB.NET 程序员是按意图编程,而不是在编码时困倦,那么当他/她完全意识到 DirectCast 不能分配不同的类型时,他/她为什么还要使用它呢?如果 VB.NET 程序员真正想要的是向下转换,他/她不应该首先尝试 DirectCast。现在 VB.NET 程序员在发现 DirectCast 不能用于向下转换时,必须退格他/她编写的内容并将其替换为 CInt(或 CType)。

于 2010-04-21T15:59:25.010 回答
1

你可以自己实现它:

static T CastTo<T>(this object obj) { return (T)obj; }

按如下方式使用它:

3.5.CastTo<int>(); //throws InvalidCastException.

这有效并且不涉及用户定义的转换器,因为泛型在运行时“解析”,但类型转换在编译时解析 - 框架实际上并没有为每个生成不同的实现T,而是共享类似T的实现,因此运行时没有解决自定义转换的信息。

于 2010-04-21T16:01:03.313 回答
1

VB.NET:

Dim xxx as label = Directcast(sender, label)

C#:

label xxx = (label)sender;
于 2019-08-28T18:14:35.643 回答
0

在 C# 中有两种类型的强制转换。如果没有其他代码,则没有与 C# 中的 DirectCast 关键字等效的东西。在不自己创建的情况下,您最接近的是使用().

你有:

My_Object c = (My_Object)object

My_Object c = object as My_Object

在第一个中,如果转换失败,则会引发错误。你是在说,“我知道这个物体是什么,如果不是,那就有问题了。”

在第二个中,c尽可能为 null 分配值(null 不能分配给值类型)。在这个中,您说“我想我知道这个是什么,但如果不是,请不要抛出错误,因为可能没有任何问题。”

其他解释铸造的帖子:

显式类型转换和隐式类型转换有什么区别?

于 2010-04-21T14:38:41.163 回答
0

您是否真的尝试运行您的示例代码?

关于...

//This code uses a type converter to go across an inheritance tree
string s = "10";
int i = (int)s;

...你假设它会运行。它也不运行。

于 2010-04-21T15:28:38.270 回答
0

让我尝试一下。

首先,让我明确一点。这不会编译:

//This code uses a type converter to go across an inheritance tree
string s = "10";
int i = (int)s;

VB的CType

在 VB 中,您将使用:

Dim s as String = "10"
Dim i as Integer = CType(s, Integer)

在 C# 中,我会使用:

string s = "10";
int i = Convert.ToInt32(s);

VB 的 DirectCast

如果它是正确的类型,则强制转换它,否则抛出 InvalidCastException。

直接施法只能上或下一个分支,永远不会跨越到另一个分支。

从那个解释来看,它直接相当于 C# 演员表。但是在 C# 中,您只需要指定强制转换运算符仅用于强制转换。铸造是完全可选的。例子:

// casting down
object t = "some random string needing to be casted down";
string s = (string) t;
// casting up
object a = s;
// explicitly casting up although it's totally optional
object b = (object) s;

C# cast 不寻找任何类型转换器。它只会为您尝试转换为的类型查找任何已定义的显式/隐式运算符重载。


VB 的 TryCast

您已经正确理解这相当于 C# 作为关键字。

于 2010-04-21T15:42:15.467 回答
0

DirectCast并且()并不总是生成相同的 CIL,所以我认为这是 VB 和 C# 编译器之间的区别。

As far as I can tell, reference types get cast using the castclass CIL instruction, while for value types the compiler generates the relevant CIL depending on the input types.

In C#, casting from double to integer emits the conv.i4 CIL instruction, which will cheerfully overwrite the sign bit or whatever in the output, if the value is too large. In Visual Basic, it's a compilation error.

Interestingly, if you use an intermediate object variable to hold the double, then the cast will fail for both C# and Visual Basic... but at runtime. Both compilers emit an unbox instruction instead of trying to do a conversion.

于 2018-09-19T08:16:51.757 回答
-1

() 铸件应该是相同的;它抛出一个 InvalidCastException。只需在 C# 中试试这个:

 string t = "hello";
 object x = t;
 int j = (int) x;
于 2010-04-21T14:38:25.163 回答
-1

我认为这种情况最好地总结了为什么 DirectCast 对非对象(对象关键字)类型的编译时类型检查安全性有一种错误的感觉,并且只是为了退格。

float f = 10;
long l = f;
Option Strict On
Dim f As Single = 10
Dim l As Long = f

AC# 编码器在发现 float 不能直接分配给 long 并且不会编译时,将执行以下操作:

long l = (long)f;

哪个是对的。

现在,让我们转向我们的 VB.NET 编码器,在发现 float 不能分配给 long 并且不会编译时,将尝试这样做:

Dim l As Long = DirectCast(f, Long)

几秒钟后……

VB.NET 程序员:“请让我听命,请编译,请……!!!”

经过一些谷歌搜索和 MSDN 浏览片刻之后:

VB.NET 程序员:“啊……所以我必须使用这个 CLng 或 CType 构造来转换变量”

Dim l As Long = CLng(f)

这就是我所说的 DirectCast 对编译时类型检查安全性的错误理解。如果程序员不知道应该在何时何地使用 DirectCast,则它只是为了退格。DirectCast 是一种不会一直佩戴的安全毯。

如果根本不使用 DirectCast,在这种情况下它有多大用处?


[编辑]

@朱尔斯

我并不是说所有的 VB.NET 程序员都不知道 DirectCast 的真正用途是什么。他们中的一些人确实知道 DirectCast 仅用于对象类型(以及装箱在对象中的原始类型)。

VB.NET 编码人员将现有 C# 代码重新编码为 VB.NET 的一种情况将得出错误的结论,即预期(无论是否正确)语言彼此对称。

当他/她在代码中看到这个构造...

TextBox txt = (TextBox)sender;

...他/她会将其翻译为:

Dim txt As TextBox = DirectCast(sender, TextBox)

哪个是对的。

现在,因为我们程序员喜欢对称性,所以我们中的一些人(如果我不知道 CLng,我可能也是)倾向于转换这段代码......

/* Numbers are stored in file as float(component's file structure
is designed by 3rd party company) */
float f = file.ReadFloat(0);
long l = (long)f; // But we don't care about using the fractional part

...对此:

Dim f As Single = file.ReadFloat(0)
Dim l As Long = DirectCast(f, Long)

如果一个 C# 人是将 C# 代码转换为 VB.NET 的人,他会因为这里明显缺乏对称性而感到沮丧。

但是对于负责将 C# 代码转换为 VB.NET 的 VB.NET 人员,他会得到这样的印象,即 C# 编译器不会捕获不兼容的类型分配,而 VB.NET 会捕获它。现在,对于这个明显的发现,他将向他的同事和一些论坛吹嘘 VB.NET 的特性。

但以免 VB.NET 程序员错误地推断出第一个代码的意图。上面的 C# 代码片段是这样开始的,最初是这样写的:

float f = file.ReadFloat(0);
long l = f;

而且这不会编译,C# 编译器会捕获不兼容的类型分配,就像等效的 VB.NETOption Strict On也不会编译它一样(尽管只有在Option Strict设置为时才不会编译On,太宽松了)。所以我们需要将 float 类型转换为 long 使用(long). 变成这样:long l = (long)f;

现在将一种变量类型转换为另一种兼容类型,与我们转换此代码的方式相同...

TextBox txt = (TextBox)sender;

...到此代码:

Dim txt As TextBox = DirectCast(sender, Textbox)

我们必须转换这段代码......

long l = (long)f; // Will compile

...到此代码:

Dim l As Long = DirectCast(f, Long) ' Will not compile

但是,唉,这不会编译。在兼容的原始类型之间进行转换时,这就是 DirectCast 的不足之处。它与上面的 C# 代码没有任何对称性,并且它不能用于转换兼容的原始类型,尽管它的名称是 Direct Cast

在我看来,DirectCast 应该被命名为CastObject,因为无论如何它只能在对象类型(以及装在对象中的原始类型)之间进行转换。DirectCast 确实与分配兼容的原始类型(整数、双精度以及它们的较低和较高对应物)无关。在兼容的原始类型之间进行分配时,DirectCast 不再有用,尤其是您无论如何都会退格它,并用适当的类型替换它。

或者我看到的另一种方式,应该修改 DirectCast 构造,因此它可以像新旧语言那样转换兼容类型,例如,C、C++、C#、Java、Delphi、D 等。这样做,在类型转换方面,它将为 VB.NET 提供与其他语言的显着对称性。这样做,我们也可以丢弃(仅假设,我们不能使依赖旧函数的其他程序失败)所有名称不直接映射到其类型的函数(例如,CInt、CDbl、CSng 等)。 . 我们将只使用 DirectCast 来代替它们。

于 2010-04-21T19:01:37.640 回答