Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/batch-file/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 强制转换时避免NullReferenceException的首选实践?_C#_Casting_Nullreferenceexception - Fatal编程技术网

C# 强制转换时避免NullReferenceException的首选实践?

C# 强制转换时避免NullReferenceException的首选实践?,c#,casting,nullreferenceexception,C#,Casting,Nullreferenceexception,我们希望避免出现NullReferenceException。目前我们有: ISomeInterface interface = this.GetISomeInterfaceInstance(); (interface as ClassImplmentsISomeInterface).Method(); 这工作正常,但存在风险NullReferenceException。一种解决办法是: ISomeInterface interface = this.GetISomeInterfaceInst

我们希望避免出现
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();
但是我推测,
实际上在背景中使用了
作为
,因此做了两次演员,这是我想要避免的。这有关系吗?例如,C#编译器是否足够聪明,可以优化这个性能问题

我还缺少其他一些技巧吗?还是以上方法之一更可取?

我通常会赞同

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

if(myInterface != null)
 myInterface.Method();

如果你想投机取巧,建议你选择第一个选项(
as
null
check)。但是,你也应该考虑重构代码,这样就不再需要强制转换了。您可以封装强制转换机制并返回NullObject。但同样,只有在适合您的场景时才使用它


您可以使用代理模式以以下方式模拟
NullObject

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

但是要确保你真的需要这样的设计:对amny类型的检查通常意味着有一些东西需要重构。

我可以想出一个很好的方法来缩短你的代码:

public static void InvokeIfIsT<T>(object o, Action<T> action)
{
    if (o is T)
       action((T)o);
}
publicstaticvoidinvokeFist(对象o,动作)
{
if(o是T)
作用((T)o);
}
这样使用:

ISomeInterface interface = this.GetISomeInterfaceInstance();
InvokeIfIsT<ClassImplmentsISomeInterface>(interface, i => i.Method());
ISomeInterface interface=this.GetISomeInterfaceInstance();
invokeFist(接口,i=>i.Method());

我个人只使用了一个
!=null
测试我是否认为某些内容可能为null。但是如果是因为你要把一个接口对象转换成一个具体的对象,那么我认为你必须后退一步,重新评估你的设计。如果为了使用接口对象而必须将接口对象强制转换为具体对象,那么我认为自己失败了

使用工厂来获取实现接口的对象,但为了使用它,必须将其强制转换为具体对象,这是禁忌。如果接口指定了一个方法,那么您不应该关心实现接口的对象是什么类型的。如果您关心该方法的作用,那么您应该创建一个不同的接口,并让您的具体类实现它


请记住,类可以实现任意数量的接口。

正确的是,
is
使用
as
,如果结果不是
null
,则返回布尔值
true
(反之亦然)

然而,我建议您将resharper规则作为一个提示或指南,而不是一个要遵循的规则(我实际上会关闭它),我这样说是因为抛出NullReferenceException没有什么错

考虑一下你的代码

ISomeInterface interface = this.GetISomeInterfaceInstance();
(interface as ClassImplmentsISomeInterface).Method();
 //error thrown above as null reference
现在,继续将其重构为

ISomeInterface interface = this.GetISomeInterfaceInstance();
if (interface is ClassImplmentsISomeInterface)
{
    (interface as ClassImplmentsISomeInterface).Method();
}
else
{
    //else?? else what? how do you recover?
}
如果一个例外只是一个例外,那么这个例外没有什么错

现在,您可能会说“处理else场景的成本比处理异常要低”,但只有在您可以恢复的情况下,例如,如果您可以编写这样的代码,才会出现这种情况

ISomeInterface interface = this.GetISomeInterfaceInstance();

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

然后重构为空检查是值得的,因为您实际上正在做一些有用的事情。如果您的方法无法正常恢复,那么在某个地方,您的方法需要向其调用方抛出异常,说“嘿,出了什么问题”

如果您的代码无法调用
方法()
,您的代码可以取得什么有用的进展?如果答案是否定的,那么NRE可能是最好的选择。@Damien_异教徒在任何情况下,如果我们冒着NRE的风险,我们可以继续而不调用
Method()
,我们可以默默地记录并继续。如果不考虑这个问题,我不会问这样一个具体的问题很好的解决方案。我宁愿将方法命名为InvokeFisType或IFisofTypeDot,这是个好主意。不幸的是,我可能得不到好处,因为我在问题中使用的例子过于简化了。但对于任何拥有大量这类代码的人来说,这将是一件非常棒的事情。不幸的是,这个产品很古老——我在这个阶段无法执行任何实质性的重构。你是对的,这是问题的根本原因。也就是说,在一些地方已经这样做了——但由于各种原因,这不可能在所有情况下都做到。我对你深表同情。嗨,杰马尔,谢谢你深思熟虑的回答。在我发现的每一种情况下,我们都会(也可能应该)使用try-catch块,正如您最后所说的。在任何情况下,我们都可以恢复并继续。因此,问题中的第一种方法,但是使用
var
@PreetSangha有什么好处?我看不到任何新的东西。