这是两个简单的类:
class Abc { public int x; }
class Bcd { public int y; }
鉴于obj
属于object
类型,以下是针对具有某些特征的Abc
或Bcd
进行测试的几个示例:
if (obj is Abc && (obj as Abc).x > 0 && Math.Pow((obj as Abc).x, 2) > 100)
Console.WriteLine(123);
if (obj is Bcd && (obj as Bcd).y > 0 && Math.Pow((obj as Bcd).y, 2) > 100)
Console.WriteLine(234);
处理这种模式的好方法是什么:
if (obj is Abc && (obj as Abc).SomePropertyOrMethod && ...
一种方法是Is
扩展方法:
public static bool Is<T>(this object obj, Func<T, bool> pred) where T : class
=> (obj is T) && pred(obj as T);
有了这个,上面的例子可以写成:
if (obj.Is<Abc>(abc => abc.x > 0 && Math.Pow(abc.x, 2) > 100))
Console.WriteLine(123);
if (obj.Is<Bcd>(bcd => bcd.y > 0 && Math.Pow(bcd.y, 2) > 100))
Console.WriteLine(234);
这样就不会重复(obj as ...)
个表达式。
这种模式还有其他方法吗?
这pattern matching proposal似乎可以很好地处理这个问题(参见第8.1节)。
答案 0 :(得分:5)
我认为这需要一件简单的事情并使其变得复杂。扩展object
导致的名称空间污染也不是很好。此外,这是对as
的滥用。这应该是一个投掷演员(例如((Abc)obj)
)因为我们希望演员总是成功。投掷演员有一个断言和内置的一个很好的例外。它记录了预计会成功的事实。
另一种方法是声明一个变量:
var abc = obj as Abc;
if (abc != null && abc.x > 0 && Math.Pow(abc.x, 2) > 100)
Console.WriteLine(123);
看起来很简单。我认为没有问题。
这就是说扩展方法方法在你不能轻易声明变量的地方很有用,例如在某些查询中或在深层嵌套的表达式中。这通常是不受欢迎的,但有时会发生。
答案 1 :(得分:3)
我不确定最佳/最差的是什么,但(obj as Abc).x
可能会在NullReferenceException
结束时失败。我看到的一种方法是打破下面的条件检查:
Abc a = obj as Abc;
if (a != null && a.x > 0 && Math.Pow(a.x, 2) > 100)
Console.WriteLine(123);
这样,只有在obj is Abc
取得成功时,才需要a != null
true
来检查条件obj as Abc
。
答案 2 :(得分:0)
添加一些静态检查:
public static bool Is<T, TBase>(this TBase obj, Func<T, bool> pred)
where TBase : class
where T : class, TBase
{
var t = obj as T;
return t != null && pred(t);
}
像这样使用:
TypeBase obj;
...
if (obj.Is((TypeA obj) => ...))