C#: 如何使用Type执行 'as' 操作

8

我希望测试一个给定的对象是否可以转换为给定的Type

在这种情况下,我有一个对象和代表我想要将其转换为的类型的Type

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

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

基本上,我想做到这一点:

    var unboxedData = data as Type;

......但是当然你不能在as语句中使用Type,那我该怎么办呢?


4
你是想要进行“测试”,还是想要进行“投射(铸造)”? - CesarGon
6个回答

6

编辑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) {
        ...
    }
    ...
}

2
如果它没有实现IConvertible接口呢? - IEHaterNumber1
实际上,我使用了 Convert.ChangeType,因为我记得在试图直接转换时遇到编译器错误。我刚刚测试了 (T)data 可以正常工作,所以我将代码更改为它。如果包含泛型类型约束 where T : class,也可以使用 data as T - mellamokb
这似乎不能解决问题,因为他从一个类型变量开始。请参见:https://dev59.com/yXVC5IYBdhLWcg3whRgw 你不能将Type的实例传递给泛型。 - deepee1
1
@deepee1:如果传递一个“Type”对象是必需的,那么这是正确的。但是,OP的帖子并不清楚这是否是任意的或者是必要的要求。 - mellamokb
通过移除 Convert.ChangeType,您不再使用 Type 进行转换。这是必要的要求。 - IEHaterNumber1
我要说,如果没有反射或泛型,这是不可能的。使用反射时,您没有编译时检查,并且必须使用反射(或dynamic)进一步调用对象的方法/属性。使用泛型时,您不能仅使用Type对象到达那里。随你选择。是否有可能重构您的调用代码以允许泛型? - mellamokb

3

......但是你不能在as语句中使用Type,那么该怎么办?

更重要的是,你不能这样使用var。所以这里没有任何收获。

你可以用以下方式测试它是否为正确的类型:

 if (expected.IsInstanceOfType(data))

但是你仍然无法编写任何良好的代码来访问 data 上的属性或方法。

2

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

Hexagon hex2 = frank as Hexagon;

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

除了使用 as 关键字之外,C# 语言还提供了 is 关键字来判断两个项是否兼容。不过,与 as 关键字不同的是,如果类型不兼容,is 关键字返回 false 而非空引用。
if (emp is SalesPerson)
{
    Console.WriteLine("{0} made {1} sale(s)!", emp.Name, 
                                              ((SalesPerson)emp).SalesNumber);
}

2
你没有使用“Type”。你正在显式定义类型。 - IEHaterNumber1
1
你正在使用 var,所以无需使用 "as" 运算符。此外,如果您想将类型 A 的变量分配给继承自 A 的类型 B 的变量,则可以使用 type a = type B as type A - Pankaj Upadhyay
2
@Pankaj 我认为你误解了:OP正在传递一个 .NET Type 类的实例,它是给定类型的元数据表示。OP想知道如何将对象的类型与 Type 表示的类型进行比较。 - Dan J

1
if (data.GetType() == t || data.GetType().IsSubclassOf(t))
{
//do your thing
}

应指出它是精确匹配还是子类匹配(以便可以将其强制转换为子类)。

1
这很棘手。问题在于var并不意味着“变量”。它更像是C#用实际类型填充的临时占位符,一旦可以从表达式中推断出类型信息,就会被替换为实际类型。unboxedData仍然是一个强类型变量。只是编译器试图找出类型,而不是您明确指定它。非常重要的是要注意,类型仍然发生在编译时而不是运行时。
如果您想在运行时动态转换对象,则无法使用var或任何其他具体类型说明符。
您的选择仅限于以下两种可能的声明之一:
  • object
  • dynamic
基于我认为您想要对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 并且具有到目标类型的转换路径。
  • 要转换的值是目标类型的子类。
  • 要转换的值在其类声明中定义了 显式转换运算符

我本来想写一个答案,但是你的更好。+1 - Davy8

0

好的,看了一圈我找到了一些东西... 如何检查是否存在隐式或显式转换?

请注意,我并没有进行过太多测试,但乍一看似乎很有前途。一个很大的缺点是如果无法进行转换它会抛出异常:

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

另一个有用的链接,采用相同的方法:使用.NET检查类型是否支持隐式或显式类型转换到另一种类型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接