2

我们希望避免出现NullReferenceException. 目前我们有:

ISomeInterface interface = this.GetISomeInterfaceInstance();
(interface as ClassImplmentsISomeInterface).Method();

这工作正常,但有风险NullReferenceException。一种解决方案是:

ISomeInterface interface = this.GetISomeInterfaceInstance();
ClassImplmentsISomeInterface instance = interface as ClassImplmentsISomeInterface;
if (instance != null)
    instance.Method();

但这会产生很多额外的代码来进行简单的检查(根据 resharper 的说法,有 100 种可能的 NRE。)第二种解决方法是:

ISomeInterface interface = this.GetISomeInterfaceInstance();
if (interface is ClassImplmentsISomeInterface)
    (interface as ClassImplmentsISomeInterface).Method();

但我收集到它is实际上as在后台使用,因此做了两次演员表,我想避免这种情况。这有关系吗?例如,C# 编译器是否足够聪明以优化此性能问题?

我在这里还缺少其他一些技术吗?还是上述方法之一更可取?

4

7 回答 7

4

I could think of a nice method that will make your code shorter:

public static void InvokeIfIsT<T>(object o, Action<T> action)
{
    if (o is T)
       action((T)o);
}

Use it like that:

ISomeInterface interface = this.GetISomeInterfaceInstance();
InvokeIfIsT<ClassImplmentsISomeInterface>(interface, i => i.Method());
于 2012-08-16T09:35:43.953 回答
2

You are correct in that is uses as and returns a boolean true if the result is not null (and vice versa)

However I would suggest you take the resharper rule as a hint or guideline rather than a rule to be followed (I would actually turn it off), and I say that because there is nothing wrong with a NullReferenceException being thrown

consider your code

ISomeInterface interface = this.GetISomeInterfaceInstance();
(interface as ClassImplmentsISomeInterface).Method();
 //error thrown above as null reference

now you go ahead and refactor it to

ISomeInterface interface = this.GetISomeInterfaceInstance();
if (interface is ClassImplmentsISomeInterface)
{
    (interface as ClassImplmentsISomeInterface).Method();
}
else
{
    //else?? else what? how do you recover?
}

There is nothing wrong with an exception if an exception is just that, an exception

Now you might say "handling the else scenario is less costly than an exception" but this is only the case if you can recover, for example, if you could write something like this

ISomeInterface interface = this.GetISomeInterfaceInstance();

try
{
    (interface as ClassImplmentsISomeInterface).Method();
}
catch (NullReferenceException)
{
    interface = this.GetIOtherInterface().Method();
}

then it would be worth refactoring into a null check as you are actually doing something useful. If your method can't recover gracefully then somewhere your method needs to throw an exception to it's caller to say "hey, something went wrong"

于 2012-08-16T09:42:41.423 回答
1

I personally just use a != null test if I think something might be null. But if it is because you're casting an Interface object to a concrete object then I think you have to take a step back an re-evaluate your design. I deem myself to have failed if I have to cast an Interface object to a concrete object in order to use it.

Using a factory to get an object that implements an interface, but then having to cast it to a concrete object in order to use it is taboo IMHO. If the interface specifies a method then you should not care what sort of object implements the interface. If you care about what the method does, then you should create a different interface and have your concrete class implement that.

Remember that classes can implement as many interfaces as you like.

于 2012-08-16T09:39:08.063 回答
0

我通常会去

ISomeInterface variable = this.GetISomeInterfaceInstance();
var myInterface = variable as ClassImplmentsISomeInterface;

if(myInterface != null)
 myInterface.Method();
于 2012-08-16T09:18:01.423 回答
0

If you want to cast speculatively then your first option (as and null check) is recommended. However, you should also consider refactoring the code such that the cast is no longer required.

于 2012-08-16T09:18:08.510 回答
0

In some cases pattern NullObject can deal with it. You can encapsulate casting mechanisms and return NullObject. But again, use it only if it fits your scenario

Null Object pattern wiki page

于 2012-08-16T09:20:52.937 回答
0

You can use the proxy pattern to simulate a NullObject in this way:

class ClassImplmentsISomeInterfaceProxy:ClassImplmentsISomeInterface
{
      ClassImplmentsISomeInterface target;
      ClassImplmentsISomeInterfaceProxy(ISomeInterface actual)
      {
          target=actual as ClassImplmentsISomeInterface;
      }
      //method implementations, with check for null
}

But ensure you really need such design: to amny type checks usually means there is something to refactor.

于 2012-08-16T09:23:25.010 回答