模式的方法:“if(obj是Abc&&(obj as Abc)......)”

时间:2015-08-02 20:32:07

标签: c#

这是两个简单的类:

class Abc { public int x; }
class Bcd { public int y; }

鉴于obj属于object类型,以下是针对具有某些特征的AbcBcd进行测试的几个示例:

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节)。

3 个答案:

答案 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) => ...))