【问题标题】:C#: How to perform 'as' operation with a TypeC#:如何使用类型执行“as”操作
【发布时间】:2011-09-21 18:36:59
【问题描述】:

我想测试给定的object 是否可以转换为给定的Type

在这种情况下,我有一个对象,Type 表示我想将其转换为什么类型:

public function FooBar(..., object data, Type expected) {
    ...
    var unboxedData = ?
    if (unboxedData == null) {
       ....
    }
    ...
}

如何将data 转换为type 所代表的类型?

基本上,我想这样做:

    var unboxedData = data as Type;

...但是你当然不能将Typeas 语句一起使用,那我该怎么办?

【问题讨论】:

  • 你想测试,还是想cast

标签: c# .net casting type-conversion


【解决方案1】:

编辑 2: 我要说的是,如果没有反射或泛型,这是不可能的。使用反射,您没有编译时检查,并且必须使用反射(或dynamic)来进一步调用对象的方法/属性。使用泛型,您不能使用 Type 对象单独到达那里。任你选。是否可以重构您的调用代码以允许泛型?


如果允许,使用泛型方法可能更容易处理:

public resultType FooBar<T>(..., object data) {
    ...
    T unboxedData = (T)data;
    ...
}

编辑:此外,如果您包含where T : class 的泛型类型约束,您可以使用data as T

public something FooBar<T>(..., object data)
    where T : class
{
    ...
    T unboxedData = data as T;
    if (unboxedData == null) {
        ...
    }
    ...
}

【讨论】:

  • 如果它没有实现 IConvertible 怎么办?
  • 实际上,我有Convert.ChangeType,因为我记得在尝试直接强制转换而不进行不必要的调用时遇到编译器错误。我刚刚测试过,(T)data 工作正常,所以我把它改成了那个。如果您包含where T : class 的泛型类型约束,您也可以使用data as T
  • 这似乎并没有解决问题,因为他从一个Type变量开始。请参阅:stackoverflow.com/questions/266115/… 您不能将 Type 的实例传递给泛型。
  • @deepee1:如果传递 Type 对象是必需的,那就是真的。 OP 帖子不清楚这是任意的还是必要的要求。
  • 通过删除Convert.ChangeType,您将不再使用Type 进行转换。这是必要的要求。
【解决方案2】:

...但是你当然不能将 Type 与 as 语句一起使用,那我该怎么办呢?

更重要的是,您不能以这种方式使用var。所以这里没有什么可得的。

你可以测试如果它是正确的类型

 if (expected.IsInstanceOfType(data))

但是您仍然无法编写任何体面的代码来访问data 上的属性或方法。

【讨论】:

    【解决方案3】:

    C# 提供 as 关键字来快速 在运行时确定给定类型是否与另一个兼容。当您使用 as 关键字时,您可以通过检查 null 返回值来确定兼容性。考虑以下几点:

    Hexagon hex2 = frank as Hexagon;
    
    if (hex2 == null)
        Console.WriteLine("Sorry, frank is not a Hexagon...");
    

    除了 as 关键字,C# 语言还提供了 is 关键字来判断两个项是否兼容。然而,与 as 关键字不同的是,如果类型不兼容,is 关键字返回 false,而不是 null 引用。

    if (emp is SalesPerson)
    {
        Console.WriteLine("{0} made {1} sale(s)!", emp.Name, 
                                                  ((SalesPerson)emp).SalesNumber);
    }
    

    【讨论】:

    • 您没有在任何地方使用Type。您正在明确定义类型。
    • 您使用的是var,所以无论如何您都不需要使用“as”运算符。此外,如果你想分配一个类型 a 给一个继承自 A 的类型 B 的变量,那么你可以使用 type a = type B as type A
    • @Pankaj 我想你误解了:OP 传入了.NET Type class 的一个实例,它是给定类型的元数据表示。 OP 想知道如何将对象的类型与 Type 表示的类型进行比较。
    【解决方案4】:
    if (data.GetType() == t || data.GetType().IsSubclassOf(t))
    {
    //do your thing
    }
    

    应该告诉你它是完全还是它的子类(所以它可以被强制转换)。

    【讨论】:

      【解决方案5】:

      这很棘手。问题是var 并不意味着“变体”。一旦可以从表达式中推断出类型信息,它的行为更像是 C# 用实际类型填充的临时占位符。 unboxedData 仍然是一个强类型变量。它只是编译器试图找出类型而不是你明确指定它。至关重要的是要注意,键入仍然发生在编译时而不是运行时。

      如果您想在运行时动态转换对象,那么您将无法使用var 或任何其他具体类型说明符。

      您的选择仅限于两种可能的声明之一:

      • 对象
      • 动态

      基于我认为你想对unboxedData 做的事情,我怀疑dynamic 是你想要走的路线,因为它允许你调用目标Type 上的任何方法.

      这就是我想出的。

      public void FooBar(object value, Type expected)
      {
        dynamic unboxedData = expected.FromObject(value);
        unboxedData.CallSomeMethodDefinedInTheTargetType(); // This will work.
      }
      

      这需要以下扩展方法。

      public static class TypeExtension
      {
          public static object FromObject(this Type target, object value)
          {
              var convertable = value as IConvertible;
              if (convertable != null)
              {
                  return convertable.ToType(target, null);
              }
              Type type = value.GetType();
              if (target.IsAssignableFrom(type))
              {
                  return value;
              }
              MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
              foreach (MethodInfo mi in methods)
              {
                  if (mi.ReturnType == target)
                  {
                      try
                      {
                          return mi.Invoke(null, new object[] { value });
                      }
                      catch (TargetInvocationException caught)
                      {
                          if (caught.InnerException != null)
                          {
                              throw caught.InnerException;
                          }
                          throw;
                      }
                  }
              }
              throw new InvalidCastException();
          }
      }
      

      如果满足以下条件之一,该演员将起作用。

      • 要转换的值实现了IConvertible,并且有到目标类型的转换路径。
      • 要转换的值是目标类型的子类。
      • 要转换的值在其类声明中定义了一个explicit conversion operator

      【讨论】:

      • 我打算写一个答案,但你的要好得多。 +1
      【解决方案6】:

      嗯,环顾四周,我发现了一些东西...... How to check if implicit or explicit cast exists?

      请注意,我没有对其进行太多测试,但乍一看似乎很有希望。一个很大的缺点是如果它不能转换它就会抛出异常:

          static bool isConvertableTo(object o, Type t)
          {
              try
              {
                  var expr = Expression.Constant(o);
                  var res = Expression.Convert(expr, t);
                  return true;
              }
              catch { }
              return false;
          }
      

      另一个使用相同方法的有用链接:Checking if a type supports an implicit or explicit type conversion to another type with .NET

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-31
        • 1970-01-01
        相关资源
        最近更新 更多