【问题标题】:C#'s equivalent to VB.NET's DirectCastC# 相当于 VB.NET 的 DirectCast
【发布时间】:2011-02-10 15:33:48
【问题描述】:

C# 是否具有与 VB.NET 的 DirectCast 等效的功能?

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

明确地说,这些关键字执行以下操作;

CType/() 转换:如果它已经是正确的类型,则转换它,否则寻找类型转换器并调用它。如果没有找到类型转换器,则抛出 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)

【问题讨论】:

  • +1 用于定义关键字而不是让人们查找它。
  • string s = "10"; int i = (int)s; 在 C# 中无法编译。
  • @csauve:你搞错了关键字的作用。虽然int i = (int)d; 可以像示例代码中那样对已知类型执行已知转换,但在您实际 在VB.NET 中使用DirectCast 的任何场景中(即,当对象的类型为在编译时未知),在 C# 中使用() 进行强制转换 执行转换——它只是 强制转换。所以一个装箱的双精度不能作为一个整数拆箱。因此,两者实际上是等效的。
  • 出于好奇,您为什么需要这个?
  • 您关于()DirectCast 有何不同的陈述对于value 类型是正确的,但对于reference 类型是不正确的。 [这两者对于引用类型是等价的;不执行任何转换。] 这使得比较毫无意义,因为恕我直言,没有理由将DirectCastvalue 类型一起使用。我的看法:() 相当于 CType 值类型,DirectCast 引用类型。

标签: c# vb.net casting directcast ctype


【解决方案1】:

很明显,您想要的功能不在 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;
}

【讨论】:

  • 如果您使用is 而不是as,则可以删除T : class 要求。 static T DirectCast&lt;T&gt;(object o) { if(o != null &amp;&amp; o is T) return (T)o; else throw new InvalidCastException(); }
  • 在您的代码中,您不使用“type”参数,因此您可以省略它。或者在额外的检查中使用它: if (typeof(T) != type) throw new InvalidArgumentException(...);.
  • 我相信 DirectCast 的等价物实际上 is 在 C# 中可用——只是根据场景分为两种不同的用法。看我的回答。
  • @Robert Davis:实际上,o 的类型检查根本没有必要,因为如果类型不匹配,(T)o 已经抛出 InvalidCastException。考虑到这一点,真正的等效代码将只是return (T)o;,这清楚地表明该方法本身实际上可能只是等效于(T)(object)value。看看我的回答。
  • 我不认为type is T check 做你认为它做的事。事实上,这是完全错误的。您可能正在寻找type.IsInstanceOfType(o)。将isas 结合起来也是多余的。
【解决方案2】:

第二次更新

好的,这是一个 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约束);如果调用 (T)o 不起作用,将抛出 InvalidCastException;为什么要使用 as 检查值是否匹配,而只是抛出与您开始使用 (T)o 路由时会抛出的相同异常?

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

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

有趣的观察:实际上,这个方法所做的只是将一个值装箱,然后尝试将其拆箱。换句话说,DirectCast&lt;int&gt;(12.0)(int)(object)12.0 完全相同(两者都会抛出异常)。意识到这一点,建议的 DirectCast&lt;T&gt; 方法就完全没有必要了。

现在,这是一个示例,说明 DirectCast 和使用 () 进行转换在 VB.NET 和 C# 之间是如何“不同”的:

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

所以我在 VB.NET 中维护 DirectCast,在任何实际使用它有意义的场景中(即,当在编译时不知道对象的类型时),是相同的作为 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 被键入为 System.Object,那么 C# 中的 () 操作将尝试将其拆箱。如果类型不完全匹配,这将失败;例如,如果o 是一个装箱的System.Double,那么(int)o 将抛出一个异常,因为o 必须 拆箱为System.Double,然后才能将其转换为@987654364 @(不信你自己试试看!)。


注意:以下内容不准确,因为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;

【讨论】:

  • 您的 vb 代码无法编译。我假设不是 12 作为 Directcast 的类型参数,而是你的意思是 long,但这也不能编译。还要确保选项 strict 已打开,否则您将获得大量隐式转换(vb 编译器喜欢读心)
  • @csauve:好电话。那是我的愚蠢!无论如何,看看我的第一点。我确实相信 DirectCast 与 C# 的 () 操作在类型为 System.Object 的对象上使用时完全相同。
  • @csauve:顺便说一下,关于 Option Strict... 在我最初发布的代码中,Option Infer 也同样有效。 (为了完整起见,我还是添加了类型。)
【解决方案3】:

你可以自己实现:

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

如下使用:

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

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

【讨论】:

    【解决方案4】:

    VB.NET:

    Dim xxx as label = Directcast(sender, label)
    

    C#:

    label xxx = (label)sender;
    

    【讨论】:

    • 欢迎来到 StackOverflow。请解释您的代码以及此代码如何解决问题。
    • 当您解决一个 8 年前的问题时,最好阅读完整的问题以及现有的答案。
    【解决方案5】:

    实际上,如果编译器推断出类型化变量无法转换为其他类型,则编译器只会捕获 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)
    

    关于Dan Tao's comment

    在 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 向下转换为 integer 呢?您必须记住 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)。

    【讨论】:

    • 尝试打开 Option Strict -- 我希望大多数商店都强制任何 vb 开发人员使用它进行写作。
    • @Michael:我也是这么想的;但正如 csauve 在对我的答案的评论中指出的那样,答案末尾的代码示例实际上并不等同。 long 可以在 C# 中分配给 int,但 VB.NET 不允许使用 DirectCastLong 分配给 Integer
    • @Dan Tao - 见我上面的评论
    • 我们的答案有些趋同;)但我的观点是专门针对您的代码double d = 10; int i = d;——您正确地评论说它不会编译,就像DirectCast(d, Integer) 不会编译一样; 另一方面,在 C# 中,int i = 10; long l = i; 在 C# 中编译,但 DirectCast(i, Long) 不会。否则,我认为我们完全一致。
    • @Dan Tao:关于 D​​irectCast(i, Long)。我认为 VB.NET 语言设计者不希望他们的程序员受苦。为什么他们不允许 VB.NET 程序员使用这个(他们允许)? --> Dim l As Long = i。我认为总而言之,DirectCast 只是在程序员意外使用它们时(因为错误的使用预期)被退格,而他们真正的意思毕竟是 CInt/CLng、CType 等
    【解决方案6】:

    让我尝试一下。

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

    //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# 作为关键字。

    【讨论】:

    • C# () cast 允许类型转换器。 DirectCast 没有。用双精度替换字符串,你会看到。 C# 允许 double->int 使用 () 强制转换,vb.net 不允许使用直接强制转换,只能使用 ctype。
    • () 强制转换不使用类型转换器。它作用于 IConvertible 接口。在 MSDN 的 TypeConverter 页面上亲自查看:msdn.microsoft.com/en-us/library/…。没有一个示例表明您能够使用 C# 强制转换来触发 TypeConverters 的使用。
    • 你是对的,我错了,“C# cast 不会查找任何类型转换器。它只会查找您尝试转换为的类型的任何已定义的显式/隐式运算符重载。”这使它成为了自己的一件事——它不是一个 ctype,也不是一个直接投射。
    【解决方案7】:

    在 C# 中有两种类型的强制转换。如果没有额外的代码,就没有与 C# 中的 DirectCast 关键字等效的东西。不自己创建的最接近的方法是使用()

    你有:

    My_Object c = (My_Object)object
    

    My_Object c = object as My_Object
    

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

    在第二个中,c 在可能的情况下被赋值为 null(不能将 null 赋值给值类型)。在这个你说“我想我知道这个是什么,但如果不是,不要抛出错误,因为没有什么可能是错误的。”

    其他解释铸造的帖子:

    What is the difference between explicit and implicit type casts?

    【讨论】:

    • 哎呀,我把我的术语弄混了。谢谢
    • 如上所述,() 强制转换允许类型转换器,DirectCast 不允许。
    【解决方案8】:

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

    关于...

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

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

    【讨论】:

    • 我已将其编辑为使用双打。我有一个错误的想法,即 string->int 在 c# 中是合法的。 double->int 应该工作
    【解决方案9】:

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

    据我所知,引用类型使用 castclass CIL 指令进行转换,而对于值类型,编译器会根据输入类型生成相关的 CIL。

    在 C# 中,从 double 转换为 integer 会发出 conv.i4 CIL 指令,如果值太大,它会很高兴地覆盖输出中的符号位或其他内容。在 Visual Basic 中,这是一个编译错误。

    有趣的是,如果您使用中间 object 变量来保存双精度,那么 C# 和 Visual Basic 的转换都将失败......但在运行时。两个编译器都会发出unbox 指令,而不是尝试进行转换。

    【讨论】:

      【解决方案10】:

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

       string t = "hello";
       object x = t;
       int j = (int) x;
      

      【讨论】:

      • 是的,但是 "(int) x" 实际上是在尝试将字符串转换为 int,因此如果 t = "10" 它将成功。 DirectCast 不允许类型转换器,因此即使 t = "10" 它仍然会失败。
      • @Collin Sauve:我不确定你从哪里得到这个想法,但这是错误的。如果您尝试执行 int j = (int) t where t = "10",它将无法编译。
      • 对不起。字符串只是一个不好的例子。 double->int 更好。双 d = 10; int i = (int)d;这在 c# 中一切都很好,但是如果我用 directcast(d,integer) 替换 (int)d 它在 vb.net 中不起作用
      • 我认为 Collin Sauve 并没有尝试从 VB.NET 运行假定的非 C# 对应物。如果 t = "10" (string),上面的代码也会导致 InvalidCastException。 @CollinSauve:请在做出假设之前在 Visual Studio 中运行代码
      【解决方案11】:

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

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

      C# 编码人员在发现 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# 编译器捕获 不兼容的类型赋值,同样具有Option Strict On 的等效 VB.NET 也不会编译它(尽管只有在 @ 987654332@ 设置为On,太宽松了)。所以我们需要使用(long) 将float 类型转换为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# 代码没有任何对称性,并且它不能用于转换兼容的原始类型,尽管它的名称为 DirectCast.

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

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

      【讨论】:

      • 你在说什么??我确切地知道何时以及何时不使用 DirectCast。你不妨说一个 VB.Net 编码器类型: Dim l as long = Gosub(f, Long)!
      猜你喜欢
      • 2010-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-17
      相关资源
      最近更新 更多