有一种“便宜而简单”的方法来判断一个对象是否为特定类型实现了显式/隐式转换运算符?

时间:2010-11-08 17:15:56

标签: c# casting

最好用一个例子来说明:

class Cat
{
}

class Dog
{
    public static implicit operator Cat(Dog d)
    {
        return new Cat();
    }
}

我想告诉一个任意对象,我是否可以把它投射到Cat。可悲的是,我似乎无法使用is / as运算符。

void Main()
{
    var d = new Dog();
    if (d is Cat) throw new Exception("d is Cat");
    var c1 = (Cat)d; // yes
    //var c2 = d as Cat; // Won't compile: Cannot convert type 'Dog' to 'Cat' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion

}

我希望避免使用try / catch(InvalidCastException),因为我可能会这么做很多,这会非常昂贵。

有没有办法廉价而轻松地做到这一点?

编辑:感谢答案人 - 为所有人投票,希望我可以给你所有的Tick,但是它会向Marc提供最通用的解决方案(奖金投票用于打击它iPod的)。然而,Jordao的解决方案设法磨练我所需要的东西而不是我所要求的,所以它可能就是我要去的地方。

3 个答案:

答案 0 :(得分:7)

转换运算符不会被/ as检查。你需要使用反射来检查和调用所述运算符。我会写一个通用的静态类

static class Convert<TFrom,TTo> {}

并在静态构造函数中检查方法,使用Delegate.CreateDelegate创建

Func<TFrom,TTo>

并将其存储在静态字段中。然后您可以快速访问键入的方法:

public static TTo Convert(TFrom obj) { return del(obj); }
public static bool CanConvert { get { return del != null; } }
private static readonly Func<TFrom,TTo> del;

答案 1 :(得分:4)

不幸的是,在这种情况下,即使try/catch也无法帮助您处理System.Object和接口等任意对象。 C#编译器仅考虑用户定义的转换(如果它们在编译时可见)。因此,对于System.Object和接口,它们不被考虑。

但是在上面的示例代码中,您只处理具体类型。实际classstruct类型(不是接口或System.Object)。如果您的场景只包含具体类型,那么最简单的检查方法是进行转换。如果两种具体类型之间没有可用的转换,编译器将会出错

var dog = new Dog();
var cat1 = (Cat)dog; // Compiles 
var str = "example";
var cat2 = (Cat)dog; // Does not compile

答案 2 :(得分:2)

C#中的转换运算符旨在在编译时使用,以便编译器在有意义的时候调用它们。您需要的测试类型是运行时测试,因此您将被迫使用反射并查找编译器生成的转换方法并自行调用它们。

如果你可以控制你需要这个转换运算符的类型(示例中为Dog),那么我认为你应该离开编译时转换运算符并创建一个更适合运行时的选项(你可以当然也使用两者):

interface IConvertible<out T> {
  T Convert();
}
class Cat { }
class Dog : IConvertible<Cat> {
  public Cat Convert() {
    return new Cat();
  }
}

你这样检查:

object o; 
// ... 
if (o is IConvertible<Cat>) { // you should also check if o is Cat ...
  Cat c = ((IConvertible<Cat>)o).Convert();
  // ...
}

或者这个:

object o; 
// ... 
var cc = o as IConvertible<Cat>;
if (cc != null) {
  Cat c = cc.Convert();
  // ...
}