2016-01-20 86 views
4

这是关于C#的新引入的空值检查运算符的问题。运营商'?'不能应用于类型'方法组'的操作数

假设我有这样一个接口:

interface ILogger 
{ 
    void Log(string message); 
} 

,并期望一个功能的记录行动:

void DoWork(Action<string> logAction) 
{ 
    // Do work and use @logAction 
} 

为什么我得到下面的编译错误,如果我尝试写:

void Main(string[] args) 
{ 
    ILogger logger = GetLogger(); // Assume GetLogger() may return null 

    // 
    // Compiler error: 
    // Operator '?' cannot be applied to operand of type 'method group' 
    // 
    DoWork(logger?.Log); 
} 
+0

http://stackoverflow.com/questions/32580536/operator-cannot-be-applied-to-operand-of-type-t的可能重复 –

+0

你可以显示'GetLogger()'吗?你的'DoWork()'似乎与你面临的问题无关。 – krillgar

+0

当'logger'为空时,您会期待哪种行为?使'logAction'值为空?使'logAction'忽略传递的字符串(实际上,使其成为=> {}')?不要叫'DoWork(..)'? –

回答

13

这里没有什么特别的事情与?.在这里,它的工作原理就像它用?:logger?.Log会得到与logger == null ? null : logger.Log相同的结果,只有logger只被评估一次。

问题是logger == null ? null : logger.Log在早期版本的C#中一样无效。 ?:要求一个操作数可以转换为另一个操作数的类型,但nulllogger.Log都没有类型。你必须把它写成例如logger == null ? null : (Action<string>) logger.Log

不幸的是,引入该铸造意味着没有,你可以使用简单漂亮的C#6缩短版本,同样也适用于?.logger?.Log是无效的,因为logger.Log没有一个类型,所以logger?.Log没有一个类型不过,如果它是一个没有类型的表达式,并且它不是一个方法组,那么C#就没有任何东西可以处理它。

+0

1.这是否意味着?对三元运算符来说,语法糖或多或少是有效的? 2.你有一个想法,为什么logger.Log没有类型?编译器似乎有足够的线索来说明它的类型。 –

+0

@ AhmedA.Hamid 1.只记录一次“记录器”的事实对我来说意味着把它称为句法糖是不公平的,但如果你不同意,我不会争辩。 2.方法组从不具有类型。它不能是'Action ':给定'public delegate void MyStringAction(string s);',类型'MyStringAction'将是一个同样可以接受的委托类型的例子。 C#编译器无需选择上下文就可以选择,但是一个语言设计决策已经使得C#不需要寻找太多的上下文。 – hvd

+0

我同意你的意见吗?不仅仅是语法糖。这只是你的回答给我的印象是在空检查操作员和三元操作员之间存在某种转换或连接。有没有? 再次感谢。 –

-1

添加到@hvd声明

我们也可以使用空条件运算符(?)空-合并运算符(?) 如果你写下面的代码

string userName = null; 
int len = userName.Length; 

那么

userName.Length 

会抛出异常:“unhandl在ConsoleApplication中发生了'System.NullReferenceException'类型的异常... 附加信息:未将对象引用设置为对象的实例。

,如果你使用下面的代码

string userName = null; 
int? len = userName.Length; 

你仍然会得到相同的异常

int? len = userName?.Length; 

它会返回null,不会抛出异常

但我们仍然有一个问题。问题是我使用int? (Nullable int),这是不好的,因为人们会更喜欢如果string为null,那么它应该返回0.

所以我们可以通过组合null条件运算符(?.)和null-合并运算符(? ?)

int len = userName?.Length ?? 0; 

新增

我们有空条件运算符的另一个用途了。

,当你与事件在这种情况下处理工作,我们可以使用

myCustomEventHandler .Invoke(这一点,EventArgs.Empty)是怎样的?;

其中“myCustomEventHandler”是C#6.0之前的公共事件,我们必须创建“myCustomEventHandler”的本地副本,否则在多线程应用程序中可能会引发异常。而在C#6中,我们可以直接调用“myCustomEventHandler”而不创建本地副本,并且它是线程安全的。

例如在C#C#5

var handler= this.myCustomEventHandler; 
if (handler!= null) 
    handler(…) 
myCustomEventHandler?.Invoke(e) 
+0

我不太确定这个说法:“因为人们会更喜欢如果string为null,那么它应该返回0”。这不是多么可行的工作;使用空检查运算符的默认值是这样的,你可以得到null或value。 –

+0

@ AhmedA.Hamid,我提供了一个例子,它也可以用这种方式。 我们还有另一个空条件运算符的用法。就像你在处理事件处理时一样,我们可以使用 myCustomEventHandler?.Invoke(this,EventArgs.Empty); 其中“myCustomEventHandler”是公开事件 在C#6.0之前,我们必须创建“myCustomEventHandler”的本地副本,否则它可能在多线程应用程序的情况下引发异常。而在C#6中,我们可以直接调用“myCustomEventHandler”而不创建本地副本,并且它是线程安全的。 –

0

如果你能你的界面更改为

interface ILogger 
{ 
    //void Log(string message); 
    Action<string> Log {get; } 
} 

然后你可以使用logger?.Log符号。

的ILogger实现将然后看起来像

class Logger : ILogger 
{ 
    Action<string> Log => str => { /* Do stuff with str */ }; 
} 
相关问题