且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何为null +真正的字符串?

更新时间:2023-11-06 17:55:04

奇怪的,因为这可能看起来,它只是下面的C#语言规范的规则。

这是第7.3.4:

  

在x op y形式,其中op是可重载的二元运算符,x是X型的前pression,y是Y类型的前pression,按如下方式处理:

     
      
  • 的为运算操作符op提供的X和Y候选用户定义操作的(X,Y)被确定。该集包括用X提供的候选经营者和用Y提供的候选运营商的联合,使用§7.3.5的规则,每一个决定。如果X和Y是相同的类型,或者如果X和Y是从一个共同的基类型衍生,然后共享候选运营商仅发生在将合并的设定一次。
  •   
  • 如果候选的用户定义运算符不为空,那么这将成为候选运算符集的操作。否则,predefined二元operator op实现,包括它们的提升形式,成为候选运算符集合的操作。 pdefined一个给定的操作者的实现中,$ P $都在操作员(§7.8通过§7.12)的说明指定。
  •   
  • 的第7.5.3节中的重载决策规则应用于候选运算符集相对于参数列表(X,Y)选择***的运营商,而这个操作符变为重载决策过程的结果。如果重载决策未能选出单个***运算符,则发生绑定时错误。
  •   

所以,让我们走过这反过来。

X是null类型在这里 - 或者不是一个类型的,在所有的,如果你要觉得这样的说法。它不提供任何候选人。 Y是布尔,它不提供任何用户定义的 + 运营商。所以第一步没有发现用户定义的运营商。

编译器然后移动到第二个小圆点,翻翻predefined二元运算符+实现及其提升形式。这些在规范第7.8.4上市。

如果你去翻那些predefined运营商来说,的只有的其中一个适用的字符串操作符+(字符串x,对象Y)。因此,候选集都有一个条目。这使得最后一颗子弹一点上是非常简单的...重载决议挑选一个操作员,给人一种整体的EX pression 字符串的类型。

一个有趣的一点是,即使有可用上未提及的其他类型的用户定义运营商,这将发生。例如:

  //定义富富运营商+(富富,布尔B)
富F = NULL;
富G = F +真实;
 

这很好,但它不是用于null文本,因为编译器不知道的样子。它只知道考虑字符串,因为它是一个predefined运营商在规范中明确列出。 (事实上​​,它的没有的由字符串类型定义的操作符... 1 ),这意味着,这将无法编译:

  //错误:无法隐式转换类型'字符串'到'富'
富F =空+真实;
 

其他的第二个操作数的类型会使用一些其他运营商,当然是:

 变种X =空+ 0; // x是可空< INT>
变种Y =空+ 0L; // y是可空<长>
变种Z =空+ DayOfWeek.Sunday; // z是可空< D​​AYOFWEEK>
 


1 你可能想知道的为什么的没有一个字符串+运营商。这是一个合理的问题,而我只的猜测的的答案,但认为这EX pression:

 字符串x = A + B + C + D;
 

如果字符串没有特殊外壳,在C#编译器,这将最终成为有效的:

 字符串TMP0 =(A + B);
字符串TMP1 = TMP0 + C;
字符串x = TMP1 + D;
 

这就是创建了两个不必要的中间串。但是,因为有编译器中的特殊支持,它的实际上的能够编译上面的:

 字符串x = string.Concat(A,B,C,D);
 

这会造成完全正确的长度只是一个单一的字符串,复制恰好一次的所有数据。尼斯。

Since true is not a string type, how is null + true a string ?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

What is the reason behind this?

Bizarre as this may seem, it's simply following the rules from the C# language spec.

From section 7.3.4:

An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §7.3.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
  • If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (§7.8 through §7.12).
  • The overload resolution rules of §7.5.3 are applied to the set of candidate operators to select the best operator with respect to the argument list (x, y), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs.

So, let's walk through this in turn.

X is the null type here - or not a type at all, if you want to think of it that way. It's not providing any candidates. Y is bool, which doesn't provide any user-defined + operators. So the first step finds no user-defined operators.

The compiler then moves on to the second bullet point, looking through the predefined binary operator + implementations and their lifted forms. These are listing in section 7.8.4 of the spec.

If you look through those predefined operators, the only one which is applicable is string operator +(string x, object y). So the candidate set has a single entry. That makes the final bullet point very simple... overload resolution picks that operator, giving an overall expression type of string.

One interesting point is that this will occur even if there are other user-defined operators available on unmentioned types. For example:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

That's fine, but it's not used for a null literal, because the compiler doesn't know to look in Foo. It only knows to consider string because it's a predefined operator explicitly listed in the spec. (In fact, it's not an operator defined by the string type... 1) That means that this will fail to compile:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

Other second-operand types will use some other operators, of course:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>


1 You may be wondering why there isn't a string + operator. It's a reasonable question, and I'm only guessing at the answer, but consider this expression:

string x = a + b + c + d;

If string had no special-casing in the C# compiler, this would end up as effectively:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

So that's created two unnecessary intermediate strings. However, because there's special support within the compiler, it's actually able to compile the above as:

string x = string.Concat(a, b, c, d);

which can create just a single string of exactly the right length, copying all the data exactly once. Nice.