C#的隐藏特性是什么?

1473

在我从这个问题中学到以下知识后,这个想法浮现在我的脑海中:

where T : struct

我们这些C#开发者都知道C#的基础,比如声明、条件语句、循环、运算符等等。
有些人甚至掌握了泛型, 匿名类型, Lambda表达式, LINQ等高级用法。
但是C#还有哪些隐藏的特性或技巧,即使是C#的粉丝、狂热者、专家也很少知道呢?
以下是目前已经揭示出来的特性:


关键词

属性

语法

  • ?? (合并空值) 操作符由 kokos 实现
  • 数字标记由 Nick Berardi 实现
  • where T:newLars Mæhlum 实现
  • 隐式泛型由 Keith 实现
  • 单参数 lambda 表达式由 Keith 实现
  • 自动属性由 Keith 实现
  • 命名空间别名由 Keith 实现
  • 使用 @ 的逐字字符串字面量由 Patrick 实现
  • enum 值由 lfoust 实现
  • @variablenames 由 marxidad 实现
  • event 操作符由 marxidad 实现
  • 格式化字符串括号由 Portman 实现
  • 属性访问器可访问性修饰符由 xanadont 实现
  • 条件 (三元) 操作符 (?:) 由 JasonS 实现
  • checkedunchecked 操作符由 Binoj Antony 实现
  • implicitexplicit 操作符由 Flory 实现

语言特性

Visual Studio 特性

框架

方法和属性

  • String.IsNullOrEmpty()方法由KiwiBastard提供
  • List.ForEach()方法由KiwiBastard提供
  • BeginInvoke()EndInvoke()方法由Will Dean提供
  • Nullable<T>.HasValueNullable<T>.Value属性由Rismo提供
  • GetValueOrDefault方法由John Sheehan提供

提示与技巧

  • 通过Andreas H.R. Nilsson提供的良好方法处理事件处理程序
  • John提供的大写字母比较
  • 无需反射即可访问匿名类型,由dp提供
  • Will提供的快速懒惰实例化集合属性的方法
  • roosteronacid提供的类似JavaScript的匿名内联函数

其他

296个回答

94

许多人没有意识到,他们可以使用OrdinalIgnoreCase来比较字符串,而不必执行someString.ToUpper()。这样可以消除额外的字符串分配开销。

if( myString.ToUpper() == theirString.ToUpper() ){ ... }

变成

if( myString.Equals( theirString, StringComparison.OrdinalIgnoreCase ) ){ ... }

39
这段代码很容易进行空值安全处理:var isEqual = String.Equals(a, b, StringComparison.OrdinalIgnoreCase); - Robert Giesecke
但是......这不是C#的功能,它是.Net框架的一个特性,更具体地说,是String类的一个特性。 - Jeroen Landheer

79

刚学会,匿名类型可以从变量名称推断属性名称:

string hello = "world";
var o = new { hello };
Console.WriteLine(o.hello);

这是因为匿名类型在编译后不再是匿名的。 - dipak

75

说实话,专家们根据其定义应该知道这些东西。但为了回答你的问题:内置类型表 (C# 参考)

编译器标记数字的原因是众所周知的:

Decimal = M
Float = F
Double = D

// for example
double d = 30D;

然而这些更加晦涩:

Long = L
Unsigned Long = UL
Unsigned Int = U

4
每次我处理小数时,我都不得不查找m的含义。这只是我自己的问题还是说m本身确实不太直观呢? :) - OregonGhost
47
M语法来自于旧的VB类型Money。 M == Money == Decimal。 - Nick Berardi
3
不行,任何小于Int32的内容都会被编译器根据其左侧的类型自动推断。 - Nick Berardi
2
@Nick:不错——我喜欢学习代码背后的历史。 - IAbstract
1
我记得M=十进制/D=双精度浮点数的方式就像我记得左右舷一样:starboard意味着“右”,有更多的“R”。十进制有一个M,但双精度浮点数没有。 - goldenratio
显示剩余3条评论

75

我喜欢在列表中查找东西,例如:

bool basketContainsFruit(string fruit) {
  return new[] { "apple", "orange", "banana", "pear" }.Contains(fruit);
}

与其如此,不如:

bool basketContainsFruit(string fruit) {
  return fruit == "apple" || fruit == "orange" || fruit == "banana" ||
    fruit == "pear";
}

在实践中并不常见,但将搜索主题与项目匹配的想法可以非常有用且简洁。


你知道每次调用第一个方法都会创建一个新的数组,并且调用Contains比进行几次比较要慢得多。但是,是的,它很方便 :) - codymanix
7
你可以同时拥有最好的两个选择(至少对于这个例子或任何整型类型而言),使用switch语句。以下是一个示例,但由于缺乏换行符,注释会影响可读性:switch(fruit){ case "apple": case "orange": case "banana": case "pear": return true; default: return false; } - P Daddy
2
@P Daddy - 是的,但是它确实遭受了许多额外语法的困扰。@Fowl - 如果情况下没有代码(直到被穿过的情况),则可以进行穿透。 - ljs
3
public static bool In<T>(T value, params T[] items){ return items.Contains(value); }如果果子.In("苹果", "橙子", "香蕉", "梨子")) { ... } - John Gibb
1
@Belorus 我认为你把可读性当作一门科学来对待了,但实际上它并不是。是的,这会影响性能,但很多时候它对程序整体性能没有任何影响。如果它能完成任务,即使它不必要地分配内存,那么它怎么会是“错误”的呢?可读性是非常主观的事情 - 对一个人来说简单的东西可能对另一个人来说令人震惊。 - ljs
显示剩余9条评论

73

InternalsVisibleTo 属性并不是广为人知,但在某些情况下非常有用。它允许另一个程序集能够访问定义程序集中的“internal”元素。


12
我经常使用这个来编写针对另一个程序集的内部成员的单元测试。这样可以将单元测试从部署中排除。 - Drew Noakes
这绝对是我在这个话题上最好的发现。我自己找到的给单元测试在不同程序集中访问内部方法的最佳解决方案是将这些方法设为protected,并在我的测试程序集中编写伪造类,让它们继承被测试的类。 - Mark Nelson
InternalsVisibleTo对于将受保护的内部无参数.ctor暴露给NHibernate层并同时隐藏它不让用户应用程序使用非常有用,这样用户只能使用强制提供所需数据的公共.ctor。 - Scott Rickman

72

这是C# 4.0中字符串类的一个新方法:

String.IsNullOrWhiteSpace(String value)

是时候了。


3
自己编写一个返回值为 (myString ?? "").Trim() == "" 的实用方法有什么问题? - user47322
1
@Charlie 换行符也被视为空格字符,是吗? - MPritchard
4
我现在尝试过Haack的方法后,绝对更喜欢它。他编写了字符串扩展方法AsNullIfEmpty和AsNullIfWhiteSpace。用这种方式,你可以在空值合并运算符中使用结果:SomeString.AsNullIfEmpty() ?? "默认值" - patridge
我忘记了我必须调整Haack的代码来做像@Charlie一样的事情,因为他使用了新的IsNullOrWhiteSpace。然而,我仍然喜欢能够在??操作符中使用结果。 - patridge
2
我真的不喜欢“”。你应该使用string.Empty (myString ?? string.Empty).Trim() == string.Empty,因为它更加清晰明了。 - Dan
3
就记录而言,我非常讨厌string.Empty。它除了""以外还能有什么呢?与一个被缓存的字符串相比,静态字段的性能优势微不足道:https://dev59.com/NXVC5IYBdhLWcg3wihqv - John Gibb

70

我在使用ReSharper时学到了这个技巧:

隐式方法组转换

//If given this:
var myStrings = new List<string>(){"abc","def","xyz"};
//Then this:
myStrings.ForEach(s => Console.WriteLine(s));
//Is equivalent to this:
myStrings.ForEach(Console.WriteLine);

请参阅 "C#中的隐式方法组转换" 以获取更多信息。


不过不要用Debug.WriteLine,因为它在内部使用#if DEBUG语句。 - Henrik
@AndyC:这不是关于那个的问题(ForEach只是在其他地方定义为自定义扩展方法。关于这个有一些宗教争论:)) - sehe
在我看来,以下代码更好:foreach(string s in myString) Console.WriteLine(s); - Grigory

68

在调试时,您可以在“查看”、“快速查看”、“立即窗口”中输入$exception并获取当前帧异常的所有信息。如果您开启了“1st chance exceptions”,这将非常有用!


68
  • TransactionScope和System.Transactions中的DependentTransaction是在.NET中使用事务处理的轻量级方式 - 它不仅适用于数据库事务
  • String.IsNullOrEmpty是一个让我惊讶的发现很多开发人员不知道的方法
  • List.ForEach - 使用委托方法遍历泛型列表

还有更多,但这是我能想到的三个明显的...


17
List.ForEach比foreach或for(;;)更快是完全荒谬的说法。ForEach使用方法/函数委托来实现行为。首先,这意味着缓存局部性更差,因为代码通常执行离实际循环更远(在内存中)。其次,您只需要验证慢的原因是查看生成的本机代码。与您可能想象的List.ForEach有更多的工作要做。 - John Leidegren
另一方面,Patrick Smacchia则持相反观点:ForEach比For慢。http://codebetter.com/blogs/patricksmacchia/archive/2008/11/19/an-easy-and-efficient-way-to-improve-net-code-performances.aspx - James
7
现在也有 String.IsNullOrWhitespace 函数。 - Ray
@JerryNixon 你的测试完全是荒谬的。而且你发布的计时结果甚至与你提供的代码都不相符。 - CodesInChaos
@JerryNixon 您的代码看起来图表大致正确,但我认为您没有测试您想要测试的内容。建议使用以下代码之一:using (var junk = m_Source.GetEnumerator()) for (; junk.MoveNext();) _Target1.Add(junk.Current + 1);或者使用以下代码:for (int i = 0; i < m_Source.Count; i++) // Count property - Dandy
显示剩余3条评论

66

Dictionary.TryGetValue(K key, out V value)

同时起到检查和获取的作用。不再需要;

if(dictionary.ContainsKey(key)) 
{
    value = dictionary[key];
    ...
}

你只需要这样做:

if(dictionary.TryGetValue(key, out value)) 
{ ... }

值已经被设置。


19
TryGetValue的另一个好处是,如果你的字典是同步的,就不会出现竞态条件。相比之下,ContainsKey存在这样一种情况:在两次调用之间,另一个线程可能会删除你正在查找的项。 - Guvante
4
如果键值为null,TryGetValue会抛出异常--避免异常的尝试失败了。我使用一个TryGetValue2()扩展方法来解决这个问题。 - Qwertie
3
在字典中查找空值似乎更可能是代码错误,而不是查找不存在的值。我个人很高兴它会抛出异常 ;) - John Gibb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接