Hidden Features of C#?
在我从这个问题中了解到以下内容后,我想到了这一点:
1 | where T : struct |
我们,C开发人员,都知道C的基本知识。我指的是声明、条件、循环、运算符等。
我们中的一些人甚至掌握了诸如仿制药、匿名类型、lambdas、linq……
但是,即使是C迷、瘾君子和专家都不知道C的最隐藏的特征或技巧是什么?
以下是迄今为止显示的特征:< BR>
关键词- 迈克尔·斯图姆
- 迈克尔·斯图姆
- Kokos的
using() 声明 - Kokos的
readonly - Mike Stone的
as - Ed Swangren的
as /is 。 - 火箭队改进的
as /is 。 - 死亡基金会的
default - Pzycoman的
global:: 。 - Alexcuse的
using() 区块 - Jakub的
volatile ?透平 - 雅库布的《以东记》〔13〕?透平
属性
- 迈克尔·斯图姆(Michael Stum)的《江户记》(EDOCX1)(14)
- dannysmurf的
ObsoleteAttribute 。 - Stu的
DebuggerDisplayAttribute 。 - bdukes的
DebuggerBrowsable 和DebuggerStepThrough 。 - 马西达.
ThreadStaticAttribute . - 马丁·克拉克
- Andrewburns的
ConditionalAttribute 。
句法
- kokos的
?? 运算符(coalesce nulls) - Nick Berardi的数字标记
- Lars M的
where T:new ?哈勒姆 - 基思的隐式仿制药
- 基思的单参数lambdas
- Keith的自动属性
- Keith的命名空间别名
- 用@by patrick逐字逐句的字符串文本
- 按lfoust计算的
enum 值 - @Marxidad提供的可变名称
- Marxidad的
event 运算符 - 按Portman设置字符串括号格式
- 属性访问器可访问性修饰符(按xanadont)
- Jasons的条件(三元)运算符(
?: ) - Binoj Antony的
checked 和unchecked 运算符 - Flory的
implicit and explicit 运营商
语言功能
- Brad Barker的可空类型
- Keith的匿名类型
- 《以东记》1〔30〕犹大希芒哥著
- Lomax的对象初始值设定项
- dakota中的david设置字符串格式
- Marxidad的扩展方法
- Jon Erickson的
partial 方法 - 预处理器指令
- robert durgin的
DEBUG 预处理器指令 - 运算符重载sefbkn
- 查克里特的类型推断
- Rob Gough将布尔运算符带到下一个级别
- 将值类型变量作为接口传递,不带由Roman Boiko装箱
- 由Roman Boiko以编程方式确定声明的变量类型
- Chris的静态构造函数
- 使用由roosteronacid提供的LINQ更容易进行眼睛/浓缩ORM映射
- 扎克保龄球队
Visual Studio功能
- Himadri在编辑器中选择文本块
- Dannysmurf的片段
框架
- Kiwibastard的
TransactionScope 。 - Kiwibastard的
DependantTransaction 。 - IAINMH的
Nullable 。 - diago的
Mutex 。 - AgeekTrapped的
System.IO.Path 。 - 胡安·曼努埃尔的《江户记》第1卷第39页
方法和属性
- Kiwibastard的
String.IsNullOrEmpty() 法 - Kiwibastard的
List.ForEach() 法 - Will Dean的
BeginInvoke() 、EndInvoke() 方法 - Rismo的
Nullable 和.HasValue Nullable 属性.Value - John Sheehan的
GetValueOrDefault 法
提示和技巧
- Andreas H.R.Nilsson的事件处理程序的好方法
- 约翰的大写比较
- 访问匿名类型而不被dp反射
- 一种快速的方法,可以通过will惰性地实例化集合属性
- roosteronacid的类似javascript的匿名内联函数
其他
- Kokos的Netmodules
- Duncan Smart的Linqbridge
- Joel Coehoorn的并行扩展
从本质上说,这不是C P,但我没有见过任何人真正使用
我敢打赌每个生产应用程序都有以下代码,即使它不应该:
1 | string path = dir +"\" + fileName; |
lambda和类型推理被低估了。lambda可以有多个语句,它们可以自动作为兼容的委托对象(只需确保签名匹配),如下所示:
1 2 3 4 5 6 | Console.CancelKeyPress += (sender, e) => { Console.WriteLine("CTRL+C detected! "); e.Cancel = true; }; |
注意,我没有
lambda不需要返回任何内容,并且类型推断在这种上下文中非常强大。
顺便说一句,您总是可以返回使lambda具有函数编程意义的lambda。例如,这里有一个lambda,它生成一个处理按钮的lambda。单击事件:
1 2 3 4 5 6 7 8 9 10 11 | Func<int, int, EventHandler> makeHandler = (dx, dy) => (sender, e) => { var btn = (Button) sender; btn.Top += dy; btn.Left += dx; }; btnUp.Click += makeHandler(0, -1); btnDown.Click += makeHandler(0, 1); btnLeft.Click += makeHandler(-1, 0); btnRight.Click += makeHandler(1, 0); |
注意链条:
这就是为什么我很高兴上了函数式编程课的原因:—)
除了C中的指针,我认为这是你应该学习的另一个基本的东西:—)
来自Rick Strahl:
你可以用链子锁住??运算符,以便进行一系列空比较。
1 | string result = value1 ?? value2 ?? value3 ?? String.Empty; |
别名泛型:
1 | using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>; |
它允许您使用
当你在很多地方使用相同的通用大长复杂的东西时,就使用它。
From CLR via C#:
When normalizing strings, it is highly
recommended that you use
ToUpperInvariant instead of
ToLowerInvariant because Microsoft has
optimized the code for performing
uppercase comparisons.
我记得有一次我的同事在比较前总是先把字符串改成大写。我一直想知道他为什么要这样做,因为我觉得先转换为小写更"自然"。看完这本书,我现在知道为什么了。
我最喜欢的技巧是使用空合并运算符和括号为我自动实例化集合。
1 2 3 4 | private IList<Foo> _foo; public IList<Foo> ListOfFoo { get { return _foo ?? (_foo = new List<Foo>()); } } |
避免检查空事件处理程序
向声明处的事件添加一个空委托,取消了在调用该事件之前始终检查该事件是否为空的需要,这太棒了。例子:
1 2 | public delegate void MyClickHandler(object sender, string myValue); public event MyClickHandler Click = delegate {}; // add empty delegate! |
让你这样做
1 2 3 4 | public void DoSomething() { Click(this,"foo"); } |
而不是这个
1 2 3 4 5 6 7 8 9 | public void DoSomething() { // Unnecessary! MyClickHandler click = Click; if (click != null) // Unnecessary! { click(this,"foo"); } } |
请也看到这相关的讨论和埃里克·利珀特关于这个主题的博客文章(以及可能的缺点)。
其他一切,还有
1)隐式泛型(为什么只在方法上而不在类上?)
1 2 3 4 5 | void GenericMethod<T>( T input ) { ... } //Infer type, so GenericMethod<int>(23); //You don't need the <>. GenericMethod(23); //Is enough. |
2)具有一个参数的简单lambda:
1 | x => x.ToString() //simplify so many calls |
3)匿名类型和初始化器:
1 2 3 4 5 6 7 8 | //Duck-typed: works with any .Add method. var colours = new Dictionary<string, string> { {"red","#ff0000" }, {"green","#00ff00" }, {"blue","#0000ff" } }; int[] arrayOfInt = { 1, 2, 3, 4, 5 }; |
另一个:
4)自动属性可以有不同的作用域:
1 | public int MyId { get; private set; } |
感谢@pzycoman提醒我:
5)名称空间别名(您可能不需要这种特殊的区别):
1 2 3 4 5 |
我有一段时间不知道"as"关键字了。
1 | MyClass myObject = (MyClass) obj; |
VS
1 | MyClass myObject = obj as MyClass; |
如果obj不是MyClass,则第二个将返回空值,而不是抛出类强制转换异常。
我喜欢的两件事是自动属性,这样您可以进一步折叠代码:
1 2 3 4 5 6 7 8 9 10 11 12 | private string _name; public string Name { get { return _name; } set { _name = value; } } |
变成
1 | public string Name { get; set;} |
同时对象初始值设定项:
1 2 3 |
变成
1 |
泛型类型中的"default"关键字:
1 | T t = default(T); |
如果t是引用类型,则返回"null";如果t是in t,则返回0;如果t是布尔值,则返回false。等等。
属性一般,但大部分都是debuggerDisplay。为你节省很多年。
The @ tells the compiler to ignore any
escape characters in a string.
只是想澄清一下这个…它并没有告诉它忽略转义字符,而是告诉编译器将字符串解释为文本。
如果你有
1 2 3 | string s = @"cat dog fish" |
它实际上打印为(请注意,它甚至包括用于缩进的空白):
1 2 3 | cat dog fish |
我认为C(.NET 3.5)最不受重视和鲜为人知的特征之一是表达树,尤其是与仿制药和羔羊组合时。这是Ninject和MoQ等较新的库正在使用的API创建方法。
例如,假设我想用一个API注册一个方法,而该API需要获取方法名
对于此类:
1 2 3 4 | public class MyClass { public void SomeMethod() { /* Do Something */ } } |
以前,开发人员经常看到字符串和类型(或者其他一些主要基于字符串的类型)这样做:
1 |
嗯,那太糟糕了,因为没有很强的打字能力。如果我改名为"somemethod"呢?现在,在3.5版本中,我可以用强类型的方式来实现这一点:
1 | RegisterMethod<MyClass>(cl => cl.SomeMethod()); |
其中registermethod类使用
1 2 3 4 5 6 7 8 9 10 | void RegisterMethod<T>(Expression<Action<T>> action) where T : class { var expression = (action.Body as MethodCallExpression); if (expression != null) { // TODO: Register method Console.WriteLine(expression.Method.Name); } } |
这是我现在爱上兰伯斯和表情树的一个重要原因。
我会想到"屈服"。像[DefaultValue()]这样的一些属性也是我最喜欢的。
"var"关键字有点广为人知,但您也可以在.NET 2.0应用程序中使用它(只要您使用.NET 3.5编译器并将其设置为输出2.0代码),这似乎不是很清楚。
编辑:Kokos,谢谢你指出??接线员,这真的很有用。因为谷歌搜索有点困难(比如??被忽略),这里是该操作员的msdn文档页:??操作员(C参考)
我倾向于发现大多数C开发人员不知道"可空"类型。基本上,原语可以有一个空值。
1 2 | double? num1 = null; double num2 = num1 ?? -100; |
将nullable double(num1)设置为空,然后将常规double(num2)设置为num1,如果num1为空,则设置为-100。
http://msdn.microsoft.com/en-us/library/1t3y8s4s(vs.80).aspx
关于可以为空的类型还有一件事:
1 2 3 |
它是返回string.empty。有关详细信息,请查看此链接
以下是一些有趣的隐藏C功能,以未记录的C关键字的形式:
1 2 3 4 5 6 7 | __makeref __reftype __refvalue __arglist |
这些是未记录的C关键字(即使是Visual Studio也能识别它们!)为了在仿制药之前更有效地装箱/拆箱,添加了这一点。它们与System.TypedReference结构协调工作。
还有用于可变长度参数列表的 arglist。
人们不太了解的一件事是System.WeakReference——一个非常有用的类,它跟踪对象,但仍然允许垃圾收集器收集对象。
最有用的"隐藏"特性是yield-return关键字。它不是真正隐藏的,但很多人不知道。Linq构建在这个之上;它允许通过在引擎盖下生成状态机来延迟执行的查询。陈瑞蒙最近发布了有关内部的、强硬的细节。
纯安全C语言中的C++(C++共享内存类型)
在不使用不安全模式和指针的情况下,类成员可以在类/结构中共享内存空间。考虑到以下类别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [StructLayout(LayoutKind.Explicit)] public class A { [FieldOffset(0)] public byte One; [FieldOffset(1)] public byte Two; [FieldOffset(2)] public byte Three; [FieldOffset(3)] public byte Four; [FieldOffset(0)] public int Int32; } |
您可以通过操作int32字段来修改字节字段的值,反之亦然。例如,此程序:
1 2 3 4 5 6 7 8 9 10 11 | static void Main(string[] args) { A a = new A { Int32 = int.MaxValue }; Console.WriteLine(a.Int32); Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four); a.Four = 0; a.Three = 0; Console.WriteLine(a.Int32); } |
输出:
1 2 3 | 2147483647 FF FF FF 7F 65535 |
只要添加使用System.Runtime.InteropServices;
使用@作为关键字的变量名。
1 2 3 |
如果要退出程序而不调用任何finally块或终结器,请使用failfast:
1 | Environment.FailFast() |
从方法返回匿名类型并在不进行反射的情况下访问成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Useful? probably not. private void foo() { var user = AnonCast(GetUserTuple(), new { Name = default(string), Badges = default(int) }); Console.WriteLine("Name: {0} Badges: {1}", user.Name, user.Badges); } object GetUserTuple() { return new { Name ="dp", Badges = 5 }; } // Using the magic of Type Inference... static T AnonCast<T>(object obj, T t) { return (T) obj; } |
下面是一个对正则表达式和文件路径有用的示例:
1 2 3 | "c:\\program files\\oldway" @"c:\program file ewway" |
@告诉编译器忽略字符串中的任何转义字符。
混合蛋白。基本上,如果您想向几个类添加一个特性,但不能为所有类使用一个基类,那么就让每个类实现一个接口(没有成员)。然后,为接口编写一个扩展方法,即
1 | public static DeepCopy(this IPrototype p) { ... } |
当然,也牺牲了一些清晰度。但它是有效的!
Not sure why anyone would ever want to use Nullable
though. :-)
对,错,找不到文件?
这一个并不是"隐藏"的,而是被误称的。
算法"map"、"reduce"和"filter"受到了广泛的关注。大多数人不知道的是.NET3.5添加了这三种算法,但它给它们起了非常类似SQL的名字,因为它们是Linq的一部分。
"map" => Select Transforms data
from one form into another"reduce" => Aggregate Aggregates
values into a single result"filter" => Where Filters data
based on a criteria
使用LINQ对用于获取迭代和条件的集合进行内联工作的能力是非常有价值的。值得学习的是,所有的LINQ扩展方法如何帮助您的代码更加紧凑和可维护。
1 | Environment.NewLine |
对于独立于系统的换行符。
如果您试图在字符串中使用大括号。格式表达式…
1 2 3 4 | int foo = 3; string bar ="blind mice"; String.Format("{{I am in brackets!}} {0} {1}", foo, bar); //Outputs"{I am in brackets!} 3 blind mice" |
@艾德,我有点沉默寡言,因为这只不过是吹毛求疵而已。但是,我会在您的代码示例中指出:
1 2 3 |
如果要使用"is",为什么要使用"as"进行安全强制转换?如果你已经确定obj确实是myclass,一个bog标准铸件:
1 | c = (MyClass)obj |
…永远不会失败。
同样,你也可以说:
1 2 3 4 5 | MyClass c = obj as MyClass; if(c != null) { ... } |
我对.NET内部的了解还不够,但我的直觉告诉我,这将使最多两种类型的Casts操作减少到最多一种。这两种方式都不太可能破坏处理银行;就个人而言,我认为后一种形式看起来也更干净。
也许不是一种先进的技术,但我一直看到的一种让我疯狂的技术:
1 2 3 4 5 6 7 8 | if (x == 1) { x = 2; } else { x = 3; } |
可压缩为:
1 | x = (x==1) ? 2 : 3; |
许多人没有意识到他们可以使用:ordinalAlignoreCase来比较字符串,而不必做一些string.toupper()。这将消除额外的字符串分配开销。
1 | if( myString.ToUpper() == theirString.ToUpper() ){ ... } |
变成
1 | if( myString.Equals( theirString, StringComparison.OrdinalIgnoreCase ) ){ ... } |
刚刚了解到,匿名类型可以从变量名推断属性名:
1 2 3 |
老实说,专家们应该很清楚这一点。但要回答您的问题:内置类型表(C参考)
标记数字的编译器因以下原因而广为人知:
1 2 3 4 5 6 | Decimal = M Float = F Double = D // for example double d = 30D; |
然而,这些更为模糊:
1 2 3 | Long = L Unsigned Long = UL Unsigned Int = U |
我喜欢在列表中查找以下内容:
1 2 3 | bool basketContainsFruit(string fruit) { return new[] {"apple","orange","banana","pear" }.Contains(fruit); } |
而不是:
1 2 3 4 | bool basketContainsFruit(string fruit) { return fruit =="apple" || fruit =="orange" || fruit =="banana" || fruit =="pear"; } |
在实践中并没有那么多,但是将项目与搜索主题进行匹配的想法确实非常有用+简洁。
InternalsVisibleTo属性是一个不那么知名的属性,但在某些情况下可以使用到难以置信的便利性。它基本上允许另一个程序集访问定义程序集的"内部"元素。
下面是C 4.0中字符串类的一个新方法:
1 | String.IsNullOrWhiteSpace(String value) |
是时候了。
我在使用Resharper时找到了这个:
隐式方法组转换
1 2 3 4 5 6 | //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中的隐式方法组转换"。
调试时,可以在watchquickwatchimmediate窗口中键入
- System.Transactions中的TransactionScope和DependentTransaction是在.NET中使用事务处理的轻量级方法-它也不仅仅用于数据库事务。
- string.isNullorEmpty是一个令我惊讶的发现,很多开发人员都不知道
- list.foreach-使用委托方法遍历泛型列表
还有很多,但那是我头顶上三个明显的…
字典.TryGetValue(k键,out v值)
作为支票和入住支票。而不是;
1 2 3 4 5 | if(dictionary.ContainsKey(key)) { value = dictionary[key]; ... } |
你可以这样做;
1 2 | if(dictionary.TryGetValue(key, out value)) { ... } |
值已经设置好了。
条件字符串。格式:
根据数字是正数、负数还是零,对数字应用不同的格式。
1 | string s = string.Format("{0:positive;negative;zero}", i); |
例如
1 2 3 4 5 | string format ="000;-#;(0)"; string pos = 1.ToString(format); // 001 string neg = (-1).ToString(format); // -1 string zer = 0.ToString(format); // (0) |
事件实际上是引擎盖下的委托,任何委托对象都可以有多个附加到它的函数,并分别使用+=和-=运算符从它中分离。
事件也可以通过"添加/删除"进行控制,与"获取/设置"类似,只是在使用"+"和"-"时调用它们:
1 2 3 4 5 6 7 8 9 | public event EventHandler SelectiveEvent(object sender, EventArgs args) { add { if (value.Target == null) throw new Exception("No static handlers!"); _SelectiveEvent += value; } remove { _SelectiveEvent -= value; } } EventHandler _SelectiveEvent; |
别忘了Goto。
更多的是运行时特性,但我最近了解到有两个垃圾收集器。工作站GC和服务器GC。工作站是Windows客户机版本的默认配置,但服务器在多核机器上运行更快。
1 2 3 4 5 | <configuration> <runtime> <gcServer enabled="true"/> </runtime> </configuration> |
小心。服务器GC需要更多内存。
我看不到上面的内容——直到最近我才意识到你可以做的一件事是从另一个构造函数调用一个构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Example { public Example(int value1) : this(value1,"Default Value") { } public Example(int value1, string value2) { m_Value1 = value1; m_value2 = value2; } int m_Value1; string m_value2; } |
其他未充分利用的运营商是
1 2 3 4 5 |
使用"throw;"而不是"throw ex;"保留堆栈跟踪
如果在不添加其他信息的情况下重新引发异常,请使用"throw"而不是"throw ex"。catch块中的空"throw"语句将发出特定的IL,该IL在保留原始堆栈跟踪的同时重新引发异常。throw ex"将堆栈跟踪丢失到异常的原始源。
我发现了一些隐藏的功能:
stackalloc 允许您在堆栈上分配数组- 没有显式参数列表的匿名方法,可以隐式转换为具有非out/ref参数的任何委托类型(对于事件非常方便,如前面的注释中所述)。
- 很多人不知道事件的真正含义(一对添加/删除方法,比如属性的get/set);C中的类字段事件实际上声明了变量和事件。
- 可以重载
== 和!= 运算符以返回除bool 以外的类型。奇怪但真实。 - C 3中的查询表达式翻译在某些方面非常"简单",这意味着您可以让它做一些非常奇怪的事情。
- 可以为空的类型具有特殊的装箱行为:将空值装箱为空引用,也可以从空值取消装箱为可以为空的类型。
我只是想复制没有注释的代码。所以,诀窍就是简单地按下alt按钮,然后突出显示你喜欢的矩形(例如下面)。
1 2 3 4 5 6 7 | protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e) { //if (e.CommandName =="sel") //{ // lblCat.Text = e.CommandArgument.ToString(); //} } |
在上面的代码中,如果我想选择:
1 2 3 | e.CommandName =="sel" lblCat.Text = e.Comman |
然后按alt键并选择矩形,不需要取消对行的注释。
看看这个。
@达科他州的大卫:
1 | Console.WriteLine("-".PadRight( 21, '-' ) ); |
我曾经这样做过,直到我发现String类有一个构造函数,它允许您以更清晰的方式执行相同的操作:
1 |
volatile关键字,告诉编译器一个字段可以由多个线程同时修改。
params关键字,即
1 2 3 4 5 6 7 | public void DoSomething(params string[] theStrings) { foreach(string s in theStrings) { // Something with the Strings… } } |
称为
1 | DoSomething("The","cat","sat","on","the" ,"mat"); |
一些我喜欢的事情:
-如果创建的接口类似于
1 |
您强制从此接口继承的任何内容包含无参数构造函数。它对我遇到了一些事情。
-使用匿名类型动态创建有用的对象:
1 |
最后,许多Java开发人员都熟悉类似的语法:
1 | public synchronized void MySynchronizedMethod(){} |
但是,在C中,这不是有效的语法。解决方法是一个方法属性:
1 2 | [MethodImpl(MethodImplOptions.Synchronized)] public void MySynchronizedMethod(){} |
foreach使用duck类型
转述或无耻地从Krzysztofcwalinas博客上窃取。比任何事情都有趣的琐事。
对于要支持foreach的对象,不必实现IEnumerable。也就是说,这不是约束,编译器不会检查它。检查的是那个
- 对象提供的公共方法GetEnumerator
- 不接受参数
- 返回具有两个成员的类型
- 返回布尔值的无参数方法moveNext
- 带有返回对象的getter的current属性
例如,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Foo { public Bar GetEnumerator() { return new Bar(); } public struct Bar { public bool MoveNext() { return false; } public object Current { get { return null; } } } } // the following complies just fine: Foo f = new Foo(); foreach (object o in f) { Console.WriteLine("Krzysztof Cwalina's da man!"); } |
静态构造函数。
实例:
1 2 3 4 5 6 7 8 9 10 11 12 | public class Example { static Example() { // Code to execute during type initialization } public Example() { // Code to execute during object initialization } } |
静态类:
1 2 3 4 5 6 7 | public static class Example { static Example() { // Code to execute during type initialization } } |
MSDN表示:
A static constructor is used to initialize any static data, or to perform a particular action that needs performed once only. It is called automatically before the first instance is created or any static members are referenced.
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class MyWebService { public static DateTime StartTime; static MyWebService() { MyWebService.StartTime = DateTime.Now; } public TimeSpan Uptime { get { return DateTime.Now - MyWebService.StartTime; } } } |
但是,你也可以很容易地做到:
1 2 3 4 5 6 7 8 9 | public class MyWebService { public static DateTime StartTime = DateTime.Now; public TimeSpan Uptime { get { return DateTime.Now - MyWebService.StartTime; } } } |
因此,当您实际需要使用静态构造函数时,很难找到任何实例。
msdn提供了有关静态构造函数的有用注释:
A static constructor does not take access modifiers or have parameters.
A static constructor is called automatically to initialize the class
before the first instance is created
or any static members are referenced.A static constructor cannot be called directly.
The user has no control on when the static constructor is executed in the
program.A typical use of static constructors is when the class is
using a log file and the
constructor is used to write
entries to this file.Static constructors are also useful when creating wrapper classes for
unmanaged code, when the constructor
can call the LoadLibrary method.If a static constructor throws an exception, the runtime will not
invoke it a second time, and the type
will remain uninitialized for the
lifetime of the application domain in
which your program is running.
最重要的一点是,如果静态构造函数中发生错误,就会抛出一个
System.Diagnostics命名空间中的其他几个属性非常有用。
DebuggerBrowsable将允许您从Debugger窗口中隐藏变量(我们将其用于公开属性的所有私有支持变量)。除此之外,debuggersteptthrough使调试器跳过该代码,这对于哑属性非常有用(如果您可以依赖于C 3.0编译器,则可能应转换为自动属性)。作为一个例子
1 2 3 4 5 6 7 8 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string nickName; public string NickName { [DebuggerStepThrough] get { return nickName; } [DebuggerStepThrough] set { this.nickName = value; } } |
C++CLR:
EDOCX1 3个关键字:当你想访问大数组的元素时,它是一个隐藏的武器,几乎和C++一样快(默认情况下,C强制执行的绑定检查会减慢事物的速度)。
我使用的一个方便的特性是
当需要开发人员注意时,使用Debugger对象自动中断,即使他/她没有启用异常自动中断:
1 2 3 4 | #if DEBUG if (Debugger.IsAttached) Debugger.Break(); #endif |
1 2 | using ComplicatedDictionary = Dictionary<int, Dictionary<string, object>>; ComplicatedDictionary myDictionary = new ComplicatedDictionary(); |
闭包
自从匿名委托被添加到2.0之后,我们就能够开发闭包了。它们很少被程序员使用,但提供了巨大的好处,如立即代码重用。考虑这段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | bool changed = false; if (model.Prop1 != prop1) { changed = true; model.Prop1 = prop1; } if (model.Prop2 != prop2) { changed = true; model.Prop2 = prop2; } // ... etc. |
请注意,上面的if语句执行类似的代码片段,但一行代码除外,即设置不同的属性。如果输入不同的代码行作为参数输入到
1 2 3 4 5 6 7 8 9 | bool changed = false; Action<Action> setAndTagChanged = (action) => { changed = true; action(); }; if (model.Prop1 != prop1) setAndTagChanged(() => model.Prop1 = prop1); if (model.Prop2 != prop2) setAndTagChanged(() => model.Prop2 = prop2); |
在第二种情况下,闭包允许您在lambda中限定
另一种方法是使用另一个未使用的特性,即"或等于"二进制赋值运算符。以下代码显示了如何:
1 2 3 4 5 6 7 8 9 10 11 | private bool conditionalSet(bool condition, Action action) { if (condition) action(); return condition; } // ... bool changed = false; changed |= conditionalSet(model.Prop1 == prop1, () => model.Prop1 = prop1); changed |= conditionalSet(model.Prop2 == prop2, () => model.Prop2 = prop2); |
我想说,使用某些系统类作为扩展方法非常方便,例如System.Enum,您可以执行以下操作…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | [Flags] public enum ErrorTypes : int { None = 0, MissingPassword = 1, MissingUsername = 2, PasswordIncorrect = 4 } public static class EnumExtensions { public static T Append<T> (this System.Enum type, T value) where T : struct { return (T)(ValueType)(((int)(ValueType) type | (int)(ValueType) value)); } public static T Remove<T> (this System.Enum type, T value) where T : struct { return (T)(ValueType)(((int)(ValueType)type & ~(int)(ValueType)value)); } public static bool Has<T> (this System.Enum type, T value) where T : struct { return (((int)(ValueType)type & (int)(ValueType)value) == (int)(ValueType)value); } } ... //used like the following... ErrorTypes error = ErrorTypes.None; error = error.Append(ErrorTypes.MissingUsername); error = error.Append(ErrorTypes.MissingPassword); error = error.Remove(ErrorTypes.MissingUsername); //then you can check using other methods if (error.Has(ErrorTypes.MissingUsername)) { ... } |
当然,这只是一个例子——这些方法可能需要更多的工作……
realproxy允许您为现有类型创建自己的代理。
这是非常先进的,我还没有看到其他人使用它——这可能意味着它对大多数人来说也没有那么有用——但它是其中一个很好知道的东西。
基本上,.NET realproxy类允许您创建称为透明代理的其他类型。在这种情况下透明意味着它在客户机上看起来完全像被代理的目标对象——但实际上不是:它是类的一个实例,它是从realproxy派生的。
这使您能够在客户机和在实际目标对象上调用的任何方法或属性之间应用强大而全面的拦截和"中介"服务。将这种能力与工厂模式(IOC等)结合起来,您可以返回透明代理而不是真实对象,从而允许您截获对真实对象的所有调用,并在每次方法调用之前和之后执行操作。事实上,我相信这是.NET用于跨应用程序域、进程和计算机边界远程处理的功能:.NET截获所有访问权,将序列化信息发送到远程对象,接收响应,并将其返回到代码。
也许有一个例子可以说明这是如何有用的:作为企业架构师,我为上一个工作创建了一个参考服务堆栈,它指定了跨部门的任何新WCF服务的标准内部组成("堆栈")。该模型要求(例如)foo服务的数据访问层实现
1 | IDAL<T> GetDAL<T>(); // retrieve data access layer for entity T |
该公司的数据访问策略经常受到性能方面的挑战。作为一个架构师,我不能监视每一个服务开发人员以确保他/她编写了一个性能良好的数据访问层。但是我在getdal工厂模式中可以做的是创建一个透明的DAL代理(一旦公共服务模型代码找到并加载了DLL),并使用高性能定时API来分析对DAL任何方法的所有调用。那么,对落后者进行排名只是按总时间降序排序DAL调用时间的问题。与开发概要(例如在IDE中)相比,这种方法的优势在于,它也可以在生产环境中进行,以确保SLA。
下面是我为"实体探查器"编写的测试代码的示例,这是为任何类型创建具有单行的分析代理的常用代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [Test, Category("ProfileEntity")] public void MyTest() { // this is the object that we want profiled. // we would normally pass this around and call // methods on this instance. DALToBeProfiled dal = new DALToBeProfiled(); // To profile, instead we obtain our proxy // and pass it around instead. DALToBeProfiled dalProxy = (DALToBeProfiled)EntityProfiler.Instance(dal); // or... DALToBeProfiled dalProxy2 = EntityProfiler<DALToBeProfiled>.Instance(dal); // Now use proxy wherever we would have used the original... // All methods' timings are automatically recorded // with a high-resolution timer DoStuffToThisObject(dalProxy); // Output profiling results ProfileManager.Instance.ToConsole(); } |
同样,这允许您截获客户机在目标对象上调用的所有方法和属性!在realproxy派生类中,必须重写invoke:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] // per FxCop public override IMessage Invoke(IMessage msg) { IMethodCallMessage msgMethodCall = msg as IMethodCallMessage; Debug.Assert(msgMethodCall != null); // should not be null - research Invoke if this trips. KWB 2009.05.28 // The MethodCallMessageWrapper // provides read/write access to the method // call arguments. MethodCallMessageWrapper mc = new MethodCallMessageWrapper(msgMethodCall); // This is the reflected method base of the called method. MethodInfo mi = (MethodInfo)mc.MethodBase; IMessage retval = null; // Pass the call to the method and get our return value string profileName = ProfileClassName +"." + mi.Name; using (ProfileManager.Start(profileName)) { IMessage myReturnMessage = RemotingServices.ExecuteMessage(_target, msgMethodCall); retval = myReturnMessage; } return retval; } |
难道.NET能做的事情很吸引人吗?唯一的限制是目标类型必须从MarshalByRefObject派生。我希望这对某人有帮助。
能够使枚举类型具有int以外的值(默认值)
1 2 3 4 5 | public enum MyEnum : long { Val1 = 1, Val2 = 2 } |
此外,您可以将任何数值赋给该枚举:
1 | MyEnum e = (MyEnum)123; |
我今天才知道这件事——我和C一起工作了5年!
它是命名空间别名限定符:
1 | extern alias YourAliasHere; |
可以使用它加载同一类型的多个版本。这在维护或升级场景中很有用,在这些场景中,类型的更新版本在某些旧代码中不起作用,但需要将其升级到新版本。加上一个名称空间别名限定符,编译器将允许您在代码中同时使用这两种类型。
任意嵌套范围
< BR>
1。对于更精细的范围行为成员,仅使用大括号,不使用控制语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void MyWritingMethod() { int sameAge = 35; { // scope some work string name ="Joe"; Log.Write(name + sameAge.ToString()); } { // scope some other work string name ="Susan"; Log.Write(name + sameAge.ToString()); } // I'll never mix up Joe and Susan again } |
在大的、混乱的或陈旧的成员内部(但它们不应该存在),这有助于防止使用错误的变量名。把东西放在更细的层次上。< BR>< BR>
2。用于代码美化或视觉语义例如,此XML编写代码遵循实际生成的XML的缩进级别(即,Visual Studio将相应地缩进范围括号)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | XmlWriter xw = new XmlWriter(..); //<root> xw.WriteStartElement("root"); { //<game> xw.WriteStartElement("game"); { //<score>#</score> for (int i = 0; i < scores.Length; ++i) // multiple scores xw.WriteElementString("score", scores[i].ToString()); } //</game> xw.WriteEndElement(); } //</root> xw.WriteEndElement(); |
三。模仿"with"语句
(另一个用途是将临时工作排除在主要范围之外)由patrik提供:有时用于模仿c中的vb"with statement"。
1 2 3 4 5 6 7 8 | var somePerson = this.GetPerson(); // whatever { var p = somePerson; p.FirstName ="John"; p.LastName ="Doe"; //... p.City ="Gotham"; } |
对于有眼光的程序员来说。
不是隐藏的,但我认为很多开发人员没有在可以为空的类型上使用hasValue和value属性。
1 2 3 4 | int? x = null; int y; if (x.HasValue) y = x.Value; |
我最喜欢的是
1 | global:: |
关键字用我们的第三方代码提供程序转义命名空间地狱…
例子:
1 2 | global::System.Collections.Generic.List<global::System.String> myList = new global::System.Collections.Generic.List<global::System.String>(); |
我已经通读了全部七页,但我没有读到:
连接字符串
我见过很多for循环将项目列表转换为带有分隔符的字符串。确保你不是从一个分隔符开始,也不是以一个分隔符结束,这总是一件痛苦的事情。内置的方法使这更容易:
1 |
评论中的托辞
不是真正的C功能,更像是一个Visual Studio功能。当您使用todo开始注释时,它会添加到您的Visual Studio任务列表(视图->任务列表)中。评论)
1 2 |
扩展方法符合泛型
您可以将扩展方法与泛型结合起来,当您在本主题前面提到技巧时,可以向特定接口添加扩展。
1 | public static void Process<T>(this T item) where T:ITest,ITest2 {} |
可枚举范围
只想要一个整数列表?
1 | Enumerable.Range(0, 15) |
我再想想……
类型定义
有人说他们错过了typedef,但你可以这样做。
1 | using ListOfDictionary = System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, string>>; |
宣布为
1 |
您可以在一个using语句中"使用"多个对象。
请注意,已经有一个答案说明您可以这样做:
这和我的不同。
1 2 | Console.WriteLine("Product: {0,-7} Price: {1,5}", product1, price1); Console.WriteLine("Product: {0,-7} Price: {1,5}", product2, price2); |
生产
来自Prabir的博客隐藏C功能
我喜欢关键词continue。
如果你在一个循环中遇到了一个条件,而不想做任何事情,只需继续执行循环即可。
例如。:
1 2 3 4 5 | foreach(object o in ACollection) { if(NotInterested) continue; } |
我个人最喜欢的两个,我认为很少使用:
完全访问调用堆栈:
1 2 3 4 5 6 7 8 9 10 11 | public static void Main() { StackTrace stackTrace = new StackTrace(); // get call stack StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) // write call stack method names foreach (StackFrame stackFrame in stackFrames) { Console.WriteLine(stackFrame.GetMethod().Name); // write method name } } |
所以,如果你选择第一个-你知道你在做什么。如果您正在创建一个助手跟踪函数-在最后一个函数之前创建一个函数-您将知道您的调用者。
嵌套using语句
通常我们这样做:
1 2 3 4 5 6 |
但我们可以这样做:
1 2 3 4 5 |
类似于javascript的匿名内联函数
返回字符串:
1 2 3 4 |
返回更复杂的对象:
1 2 3 4 5 6 7 8 9 |
一个真实的用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | var tr = new TableRow(); tr.Cells.AddRange ( new[] { new TableCell { Text ="" }, new TableCell { Text ="" }, new TableCell { Text ="" }, new TableCell { Text = new Func<String>(() => { return @"Result of a chunk of logic, without having to define the logic outside of the TableCell constructor"; })() }, new TableCell { Text ="" }, new TableCell { Text ="" } } ); |
注意:不能在内联函数的范围内重复使用变量名。
< BR>
可选语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // The one-liner Func<Int32, Int32, String> Add = (a, b) => Convert.ToString(a + b); // Multiple lines Func<Int32, Int32, String> Add = (a, b) => { var i = a + b; return i.ToString(); }; // Without parameters Func<String> Foo = () =>""; // Without parameters, multiple lines Func<String> Foo = () => { return""; }; |
缩短字符串并添加水平省略号…
1 | Func<String, String> Shorten = s => s.Length > 100 ? s.Substring(0, 100) +"…" : s; |
@Lomaxx前几天我还了解到(在我了解您的提示的同时),您现在可以在同一个属性上拥有不同的访问级别:
1 | public string Name { get; private set;} |
这样,只有类本身才能设置name属性。
1 | public MyClass(string name) { Name = name; } |
还有一个threadStaticAttribute使每个线程的静态字段都是唯一的,因此您可以拥有强类型的线程本地存储。
即使扩展方法不是那么秘密(Linq是基于它们的),它们对于实用工具帮助器方法的用处和可读性也可能不那么明显:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //for adding multiple elements to a collection that doesn't have AddRange //e.g., collection.Add(item1, item2, itemN); static void Add<T>(this ICollection<T> coll, params T[] items) { foreach (var item in items) coll.Add(item); } //like string.Format() but with custom string representation of arguments //e.g.,"{0} {1} {2}".Format<Custom>(c=>c.Name,"string",new object(),new Custom()) // result:"string {System.Object} Custom1Name" static string Format<T>(this string format, Func<T,object> select, params object[] args) { for(int i=0; i < args.Length; ++i) { var x = args[i] as T; if (x != null) args[i] = select(x); } return string.Format(format, args); } |
这实际上不是一个C隐藏的特性,但我最近发现了weakreference类,并被它击溃了(尽管这可能因为它帮助我找到了解决我的一个特定问题的方法而有偏见…)
一行中的按需字段初始化:
1 2 3 4 |
我不确定我对支持赋值表达式的c的感觉如何,但是,嘿,它就在这里:—)
轻松确定声明变量的类型(从我的答案中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System; using System.Collections.Generic; static class Program { public static Type GetDeclaredType<T>(T x) { return typeof(T); } // Demonstrate how GetDeclaredType works static void Main(string[] args) { IList<string> iList = new List<string>(); List<string> list = null; Console.WriteLine(GetDeclaredType(iList).Name); Console.WriteLine(GetDeclaredType(list).Name); } } |
结果:
1 2 | IList`1 List`1 |
其名称(借用"get variable name"):
1 2 3 4 5 6 7 8 9 10 11 |
结果:
从C/C++中移动的程序员可能会错过这个:
在C中,%(模数运算符)用于浮点数!
AppDomain.UnhandledException事件也是隐藏的候选事件。
This event provides notification of uncaught exceptions. It allows the application to log information about the exception before the system default handler reports the exception to the user and terminates the application. If sufficient information about the state of the application is available, other actions may be undertaken — such as saving program data for later recovery. Caution is advised, because program data can become corrupted when exceptions are not handled.
我们可以看到,即使在这个站点上,也有很多人想知道为什么他们的应用程序没有启动,为什么它崩溃等等。
environment.userInteractive属性。
The UserInteractive property reports
false for a Windows process or a
service like IIS that runs without a
user interface. If this property is
false, do not display modal dialogs or
message boxes because there is no
graphical user interface for the user
to interact with.
if debug预处理器指令。它对测试和调试单元测试路线)。
1 2 3 4 | string customerName = null; #if DEBUG customerName ="Bob" #endif |
仅当Visual Studio设置为时才执行代码块在"调试"模式下编译。否则代码块将被编译器忽略(并在Visual Studio中变灰)。
我找不到任何使用字符串的人。请使用分隔符联接到联接字符串。每个人都在写同样难看的循环
1 2 3 4 5 6 7 8 9 | var sb = new StringBuilder(); var count = list.Count(); for(int i = 0; i < count; i++) { if (sb.Length > 0) sb.Append(seperator); sb.Append(list[i]); } return sb.ToString(); |
而不是
1 | return string.Join(separator, list.ToArray()); |
C??空合并运算符-
不是真正隐藏,但很少使用。可能是因为很多开发人员在看到条件时跑了一英里?接线员,所以当他们看到这个的时候,他们会跑两个。用于:
1 | string mystring = foo ??"foo was null" |
而不是
1 2 3 4 5 | string mystring; if (foo==null) mystring ="foo was null"; else mystring = foo; |
部分方法
Charlie Calvert在他的博客上解释了部分方法
Scott Cate有一个很好的部分方法演示
C中有一些真正隐藏的关键字和特性与typedReference未记录类相关。以下关键字未记录:
__makeref __reftype __refvalue __arglist
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Create a typed reference int i = 1; TypedReference tr1 = __makeref(i); // Get the type of a typed reference Type t = __reftype(tr1); // Get the value of a typed referece int j = __refvalue(tr1, int); // Create a method that accepts and arbitrary number of typed references void SomeMethod(__arglist) { ... // Call the method int x = 1; string y ="Foo"; Object o = new Object(); SomeMethod(__arglist(x,y,o)); // And finally iterate over method parameters void SomeMethod(__arglist) { ArgIterator ai = new ArgIterator(__arglist); while(ai.GetRemainingCount() >0) { TypedReference tr = ai.GetNextArg(); Console.WriteLine(TypedReference.ToObject(tr)); }} |
正误运算符真的很奇怪。
这里可以找到更全面的例子。
编辑:有相关的问题,那么C中的假操作符有什么用?
我发现只有少数开发人员知道这个特性。
如果需要通过某个接口(由该值类型实现)使用值类型变量的方法,则在方法调用期间很容易避免装箱。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | using System; using System.Collections; interface IFoo { void Foo(); } struct MyStructure : IFoo { public void Foo() { } } public static class Program { static void MethodDoesNotBoxArguments<T>(T t) where T : IFoo { t.Foo(); } static void Main(string[] args) { MyStructure s = new MyStructure(); MethodThatDoesNotBoxArguments(s); } } |
IL代码不包含任何方框说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | .method private hidebysig static void MethodDoesNotBoxArguments<(IFoo) T>(!!T t) cil managed { // Code size 14 (0xe) .maxstack 8 IL_0000: ldarga.s t IL_0002: constrained. !!T IL_0008: callvirt instance void IFoo::Foo() IL_000d: ret } // end of method Program::MethodDoesNotBoxArguments .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 15 (0xf) .maxstack 1 .locals init ([0] valuetype MyStructure s) IL_0000: ldloca.s s IL_0002: initobj MyStructure IL_0008: ldloc.0 IL_0009: call void Program::MethodDoesNotBoxArguments<valuetype MyStructure>(!!0) IL_000e: ret } // end of method Program::Main |
参见richter,J.clr via c,第二版,第14章:接口,关于泛型和接口约束的章节。
另见我对另一个问题的回答。
几乎所有的冷门都被提到过。不知道这个是不是很有名
属性/字段构造函数初始化:
这将创建矩形,并设置列出的属性。
我注意到了一些有趣的事情——在属性列表的末尾可以有一个逗号,而不会出现语法错误。所以这也是有效的:
有几个人提到过使用积木,但我认为它们比人们想象的有用得多。把它们看作是穷人的AOP工具。我有许多简单的对象,它们在构造函数中捕获状态,然后在dispose()方法中恢复状态。这允许我将一部分功能包装在一个using块中,并确保在最后恢复状态。例如:
1 2 3 4 |
CursorState捕获窗体使用的当前光标,然后将窗体设置为使用提供的光标。最后,它将恢复原始光标。我做了很多这样的事情,例如在刷新之前捕获网格上的选择和当前行等等。
基于这个线程应该被命名为"直到最近你才知道的关于C的事情,尽管你认为你已经知道了一切",我的个人特性是异步委托。
直到我读了Jeff Richter的c/clr书(优秀的书,所有做.net的人都应该读),我才知道你可以用
关于事件处理程序的另一个注意事项:您可以简单地创建一个raise扩展方法,如下所示:
1 2 3 4 5 6 7 8 | public static class EventExtensions { public static void Raise<T>(this EventHandler<T> @event, object sender, T args) where T : EventArgs { if(@event!= null) { @event(sender, args); } } } |
然后您可以使用它来引发事件:
1 2 3 4 5 6 7 | public class MyImportantThing { public event EventHandler<MyImportantEventEventArgs> SomethingHappens; ... public void Bleh() { SomethingHappens.Raise(this, new MyImportantEventEventArgs { X=true }); } } |
这种方法具有执行编码标准(使用
一次又一次地写同样的精确函数是没有意义的。也许下一个版本的C最终会有一个可以放在扩展方法上的
编辑:基于注释删除扩展方法内的临时变量
好吧,这看起来很明显,但我想提一下
我敢肯定很多人都不知道它,或者忘记它的存在,但在某些情况下它确实有帮助。例如,当您想比较两个对象是否相等时,不知道它们是否为空。你写过多少次这样的东西:
1 2 3 4 | if ((x == y) || ((x != null && y != null) && x.Equals(y))) { ... } |
当你能写:
1 2 3 4 | if (Object.Equals(x, y)) { ... } |
(
1 | string.Empty |
我知道这不是幻想(奇怪得可笑),但我一直在使用它,而不是"。
在有人告诉你它在那里之前,它是相当隐蔽的。
那Lobservable呢?
几乎每个人都知道IEnumerable,但他们的数学对偶似乎是未知的IOBServable。可能是因为它在.NET 4中是新的。
它所做的不是提取信息(像一个可枚举的),而是将信息推送到可观察者的订户。
与RX扩展一起,它将改变我们处理事件的方式。为了说明它有多强大,请检查这里的一个很短的示例。
属性目标
每个人都见过。基本上,当你看到这个:
1 | [assembly: ComVisible(false)] |
该属性的"assembly:"部分是目标。在这种情况下,属性将应用于程序集,但还有其他属性:
1 2 | [return: SomeAttr] int Method3() { return 0; } |
在此示例中,属性应用于返回值。
我喜欢的一个很好的类是
1 | bool myFlag = System.Xml.XmlConvert.ToBoolean(myAttribute.Value); |
注意:由于XML中的布尔类型除了接受"true"和"false"作为有效值之外,还接受1和0,因此在这种情况下使用字符串比较很容易出错。
很抱歉这么晚发帖,我刚到栈溢出,所以错过了早先的机会。
我发现
我遇到的大多数C开发人员在定义自定义事件时仍然定义自定义事件处理程序委托,这不再是必需的。
而不是:
1 2 3 4 5 6 | public delegate void MyCustomEventHandler(object sender, MyCustomEventArgs e); public class MyCustomEventClass { public event MyCustomEventHandler MyCustomEvent; } |
你可以去:
1 2 3 4 | public class MyCustomEventClass { public event EventHandler<MyCustomEventArgs> MyCustomEvent; } |
这要简洁得多,而且您不会陷入这样的困境:是将委托放入包含事件的类的.cs文件中,还是将EventArgs派生类放入.cs文件中。
我参加这个聚会迟到了,所以我的第一选择已经有了。但我还没看到有人提到这个宝石:
对.NET框架的并行扩展
它有类似于用parallel.for或foreach替换parallel.foreach的内容。
平行样品:在您看来,一秒钟内可以创建多少个CLR对象?请参见Fallowing示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace ObjectInitSpeedTest { class Program { //Note: don't forget to build it in Release mode. static void Main() { normalSpeedTest(); parallelSpeedTest(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Press a key ..."); Console.ReadKey(); } private static void parallelSpeedTest() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("parallelSpeedTest"); long totalObjectsCreated = 0; long totalElapsedTime = 0; var tasks = new List<Task>(); var processorCount = Environment.ProcessorCount; Console.WriteLine("Running on {0} cores", processorCount); for (var t = 0; t < processorCount; t++) { tasks.Add(Task.Factory.StartNew( () => { const int reps = 1000000000; var sp = Stopwatch.StartNew(); for (var j = 0; j < reps; ++j) { new object(); } sp.Stop(); Interlocked.Add(ref totalObjectsCreated, reps); Interlocked.Add(ref totalElapsedTime, sp.ElapsedMilliseconds); } )); } // let's complete all the tasks Task.WaitAll(tasks.ToArray()); Console.WriteLine("Created {0:N} objects in 1 sec ", (totalObjectsCreated / (totalElapsedTime / processorCount)) * 1000); } private static void normalSpeedTest() { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("normalSpeedTest"); const int reps = 1000000000; var sp = Stopwatch.StartNew(); sp.Start(); for (var j = 0; j < reps; ++j) { new object(); } sp.Stop(); Console.WriteLine("Created {0:N} objects in 1 sec ", (reps / sp.ElapsedMilliseconds) * 1000); } } } |
我看到很多人复制了
不是很隐蔽,但很有用。当你有一个
1 2 3 4 5 6 7 | [Flags] public enum ErrorTypes { None = 0, MissingPassword = 1 << 0, MissingUsername = 1 << 1, PasswordIncorrect = 1 << 2 } |
C 3.0的LINQ查询理解是一个完整的单态理解(实际上是由Haskell的一个设计师设计的)。它们适用于遵循"LINQ模式"的任何泛型类型,并允许您使用纯单元函数样式编写,这意味着所有变量都是不可变的(就像您在using和foreach语句中使用的唯一变量是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | interface IFoo<T> { T Bar {get;} } class MyFoo<T> : IFoo<T> { public MyFoo(T t) {Bar = t;} public T Bar {get; private set;} } static class Foo { public static IFoo<T> ToFoo<T>(this T t) {return new MyFoo<T>(t);} public static void Do<T>(this T t, Action<T> a) { a(t);} public static IFoo<U> Select<T,U>(this IFoo<T> foo, Func<T,U> f) { return f(foo.Bar).ToFoo(); } } /* ... */ using (var file = File.OpenRead("objc.h")) { var x = from f in file.ToFoo() let s = new Scanner(f) let p = new Parser {scanner = s} select p.Parse(); x.Do(p => { /* drop into imperative code to handle file in Foo monad if necessary */ }); } |
我最喜欢的属性:InternalsVisibleTo
在程序集级别,您可以声明另一个程序集可以看到您的内部。为了测试的目的,这是绝对美妙的。
将其粘贴到assemblyinfo.cs或等效文件中,您的测试程序集可以完全访问所有需要测试的内部内容。
1 | [assembly: InternalsVisibleTo("MyLibrary.Test, PublicKey=0024...5c042cb")] |
如您所见,测试程序集必须具有强名称才能获得被测试程序集的信任。
在.NET Framework 2.0+、Compact Framework 2.0+和XNA Framework 1.0+中提供。
需要返回空的IEnumerable?
1 2 3 | public IEnumerable<T> GetEnumerator(){ yield break; } |
您可以使用
1 2 3 4 5 6 | { string test2 ="3"; Console.Write(test2); } Console.Write(test2); //compile error |
我喜欢使用@字符进行SQL查询。它保持了SQL的良好性和格式,并且不必用字符串分隔符将每一行包围起来。
1 2 3 | string sql = @"SELECT firstname, lastname, email FROM users WHERE username = @username AND password = @password"; |
extern alias关键字引用具有相同完全限定类型名的两个版本的程序集。
能够使用LINQ表达式执行强类型反射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | static void Main(string[] args) { var domain ="matrix"; Check(() => domain); Console.ReadLine(); } static void Check<T>(Expression<Func<T>> expr) { var body = ((MemberExpression)expr.Body); Console.WriteLine("Name is: {0}", body.Member.Name); Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member) .GetValue(((ConstantExpression)body.Expression).Value)); } // output: // Name is: 'domain' // Value is: 'matrix' |
有关如何在C中查找变量或参数名称的详细信息,请参阅。
可以在枚举中存储颜色。
1 2 3 4 5 6 | public enum MyEnumColors : uint { Normal = 0xFF9F9F9F, Active = 0xFF8EA98A, Error = 0xFFFF0000 } |
您可以在C名称中使用任何Unicode字符,例如:
1 2 3 4 5 6 7 | public class MyClass { public string H?rf?ner() { return"Yes, it works!"; } } |
甚至可以使用Unicode转义。此项相当于上述:
1 2 3 4 5 6 7 | public class MyClass { public string H\u00e5rf?ner() { return"Yes, it (still) works!"; } } |
固定语句
此语句防止垃圾收集器重新定位可移动变量。Fixed还可以用于创建固定大小的缓冲区。
The fixed statement sets a pointer to a managed variable and"pins" that variable during the execution of statement.
斯塔克洛克
stackalloc在堆栈上分配一个内存块。
您可以添加和删除键入较少的委托。
通常方式:
1 |
更少的打字方式:
1 | handler += func; |
同样有用,但不常用:受约束的执行区域。
BCL团队博客引用:
Constrained execution regions (CER's)
exist to help a developer write her
code to maintain consistency. The CLR
doesn't guarantee that the developer's
code is correct, but the CLR does
hoist all of the runtime-induced
failure points (ie, async exceptions)
to either before the code runs, or
after it has completed. Combined with
constraints on what the developer can
put in a CER, these are a useful way
of making strong guarantees about
whether your code will execute. CER's
are eagerly prepared, meaning that
when we see one, we will eagerly JIT
any code found in its
statically-discoverable call graph.
If the CLR's host cares about stack
overflow, we'll probe for some amount
of stack space as well (though perhaps
not enough stack space for any
arbitrary method*). We also delay
thread aborts until the CER has
finished running.
当以原子方式对数据结构的多个字段进行编辑时,它可能很有用。因此,在对象上进行事务处理很有帮助。
此外,CriticalFinalizerObject似乎是隐藏的(至少那些没有编写不安全代码的人)。CriticalFinalizerObject确保垃圾收集将执行终结器。在分配时,定稿器及其调用图是预先准备好的。
我经常遇到需要将一个通用参数对象持久化到基类中的viewstate中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public abstract class BaseListControl<ListType,KeyType,ParameterType> : UserControl where ListType : BaseListType && ParameterType : BaseParameterType, new { private const string viewStateFilterKey ="FilterKey"; protected ParameterType Filters { get { if (ViewState[viewStateFilterKey] == null) ViewState[viewStateFilterKey]= new ParameterType(); return ViewState[viewStateFilterKey] as ParameterType; } set { ViewState[viewStateFilterKey] = value; } } } |
用途:
1 2 3 4 5 6 7 8 9 | private void SomeEventHappened(object sender, EventArgs e) { Filters.SomeValue = SomeControl.SelectedValue; } private void TimeToFetchSomeData() { GridView.DataSource = Repository.GetList(Filters); } |
"Where ParameterType:BaseParameterType,New"的这个小技巧使它真正起作用。
在我的基类中使用这个属性,我可以自动处理分页、设置筛选值以筛选网格视图、使排序变得非常容易等等。
我真的只是说,仿制药可能是一个非常强大的野兽在错误的手中。
使用带有flagattribute和enum的"~"运算符有时,我们将使用带有枚举的标志属性对枚举执行按位操作。
1 2 3 4 5 6 7 8 9 10 11 | [Flags] public enum Colors { None = 0, Red = 1, Blue = 2, White = 4, Black = 8, Green = 16, All = 31 //It seems pretty bad... } |
注意,选项"all"的值在枚举中非常奇怪。相反,我们可以使用带标记枚举的"~"运算符。
1 2 3 4 5 6 7 8 9 10 11 | [Flags] public enum Colors { None = 0, Red = 1, Blue = 2, White = 4, Black = 8, Green = 16, All = ~0 //much better now. that mean 0xffffffff in default. } |
枚举上的FlagsAttribute如何?它允许您执行位操作…我花了很长时间才发现如何在.NET中很好地执行逐位操作。
在堆栈溢出时,我只了解了一个特性,那就是在返回参数上设置属性的能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 | [AttributeUsage( AttributeTargets.ReturnValue )] public class CuriosityAttribute:Attribute { } public class Bar { [return: Curiosity] public Bar ReturnANewBar() { return new Bar(); } } |
这对我来说真的是一个隐藏的特性:—)
您可以将多个属性放在一对方括号中:
1 2 |
标记我的结束区域…
1 2 3 4 5 | #region stuff1 #region stuff1a //... #endregion stuff1a #endregion stuff1 |
当一个类实现InotifyPropertyChanged并且希望通知绑定系统(WPF、Silverlight等)某个对象(ViewModel)的多个绑定属性已更改时,可以使用空值或String.Empty引发PropertyChanged事件。
这在msdn中有文档记录,但是代码示例和文章通常不能解释这种可能性。我发现它很有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public class BoundObject : INotifyPropertyChanged { private int _value; private string _text; public event PropertyChangedEventHandler PropertyChanged; public int Value { get { return _value; } set { if (_value != value) { _value = value; OnPropertyChanged("Value"); } } } public string Text { get { return _text; } set { if (_text != value) { _text = value; OnPropertyChanged("Text"); } } } public void Init(){ _text ="InitialValue"; _value = 1; OnPropertyChanged(string.Empty); } public void Reset() { _text ="DefaultValue"; _value = 0; OnPropertyChanged(string.Empty); } private void OnPropertyChanged(string propertyName) { PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName); if (PropertyChanged != null) { PropertyChanged(this, e); } } } |
条件属性
允许您告诉编译器在特定条件下忽略对用属性标记的方法的调用(define)。
省略方法调用的事实也意味着不计算其参数。这非常方便,它允许您在debug.assert()中调用昂贵的验证函数,而不用担心它们会减慢您的发布版本。
定义自定义属性时,可以将它们与[MyAtttribute]或[MyAtt]一起使用。当两个写入都存在类时,就会发生编译错误。
@特殊字符可用于区分它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | [AttributeUsage(AttributeTargets.All)] public class X: Attribute {} [AttributeUsage(AttributeTargets.All)] public class XAttribute: Attribute {} [X] // Error: ambiguity class Class1 {} [XAttribute] // Refers to XAttribute class Class2 {} [@X] // Refers to X class Class3 {} [@XAttribute] // Refers to XAttribute class Class4 {} |
使用LINQ更容易进行眼睛/浓缩ORM映射
考虑下这个表:
1 2 3 | [MessageId] INT, [MessageText] NVARCHAR(MAX) [MessageDate] DATETIME |
…这种结构:
1 2 3 4 5 6 | struct Message { Int32 Id; String Text; DateTime Date; } |
< BR>< BR>而不是按照以下方式做一些事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
在我看来,你可以用更少的代码行来使用LINQ,做同样的事情;更多的样式。像这样:
1 2 3 4 5 6 | var messages = DataTable.AsEnumerable().Select(r => new Message { Id = Convert.ToInt32(r["MessageId"]), Text = Convert.ToString(r["MessageText"]), Date = Convert.ToDateTime(r["MessageDate"]) }).ToList(); |
这种方法可以嵌套,就像循环一样。
表达式
1 | Func<int, int, int> add = (a, b) => (a + b); |
模糊字符串格式
1 2 3 4 5 | console.writeline("0:d10",2);/000000000 2dictionary<string,string>dict=new dictionary<string,string>{{"戴维","C."<div class="suo-content">[collapse title=""]<ul><li>lambda表达式在这里得到了更全面的解释:developer.com/net/csharp/article.php/3598381和这里:msdn.microsoft.com/en-us/library/bb397687.aspx</li></ul>[/collapse]</div><hr><P>用这个怎么样:</P>[cc lang="csharp"]#if DEBUG Console.Write("Debugging"); #else Console.Write("Final"); #endif |
当您用DEBUG定义编译解决方案时,它将输出"DEBUG"。
如果您的编译设置为发布,它将写"final"。
直到最近,我才开始真正欣赏"使用"积木。他们让事情变得更整洁。)
工厂方法的类型推断
我不知道这篇文章是否已经发布了(我扫描了第一篇文章,找不到它)。
最好用一个例子来说明这一点,假设您有这个类(模拟一个元组),为了演示所有使这成为可能的语言特性,我将一步一步地进行演示。
1 2 3 4 5 6 7 8 9 10 11 | public class Tuple<V1, V2> : Tuple { public readonly V1 v1; public readonly V2 v2; public Tuple(V1 v1, V2 v2) { this.v1 = v1; this.v2 = v2; } } |
每个人都知道如何创建它的实例,例如:
1 |
不完全是火箭科学,现在我们当然可以将变量的类型声明更改为var,如下所示:
1 |
众所周知,这里有一个带有类型参数的静态方法,每个人都应该熟悉:
1 2 3 4 | public static void Create<T1, T2>() { // stuff } |
同样,称之为"常识",是这样做的:
1 | Create<float, double>(); |
大多数人不知道的是,如果泛型方法的参数包含它所需的所有类型,则可以推断这些类型,例如:
1 2 3 4 | public static void Create<T1, T2>(T1 a, T2 b) { // stuff } |
这两个调用是相同的:
1 2 | Create<float, string>(1.0f,"test"); Create(1.0f,"test"); |
因为T1和T2是从您传递的参数推断出来的。将这些知识与var关键字结合起来,我们可以通过使用静态方法添加第二个静态类,例如:
1 2 3 4 5 6 7 | public abstract class Tuple { public static Tuple<V1, V2> Create<V1, V2>(V1 v1, V2 v2) { return new Tuple<V1, V2>(v1, v2); } } |
达到这个效果:
1 | var tup = Tuple.Create(1,"Hello, World!"); |
这意味着:变量"tup"的类型、"create"的类型参数和"create"的返回值都是从作为要创建的参数传递的类型中推断出来的。
完整代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public abstract class Tuple { public static Tuple<V1, V2> Create<V1, V2>(V1 v1, V2 v2) { return new Tuple<V1, V2>(v1, v2); } } public class Tuple<V1, V2> : Tuple { public readonly V1 v1; public readonly V2 v2; public Tuple(V1 v1, V2 v2) { this.v1 = v1; this.v2 = v2; } } // Example usage: var tup = Tuple.Create(1,"test"); |
这给你的全类型推断工厂方法无处不在!
我对扩展方法非常感兴趣,因为它们可以为现有的代码或无法编辑的代码添加非常想要的功能。我现在做的每件事都添加了一个我最喜欢的字符串。IsNullOrEmpty())
1 2 3 4 5 6 7 | public static class Strings { public static bool IsNullOrEmpty(this string value) { return string.IsNullOrEmpty(value); } } |
这样可以缩短代码的长度。
1 2 3 4 5 | var input = Console.ReadLine(); if (input.IsNullOrEmpty()) { Console.WriteLine("try again"); } |
flagsattribute,使用枚举生成位掩码时的一个小但好的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [Flags] public enum ConfigOptions { None = 0, A = 1 << 0, B = 1 << 1, Both = A | B } Console.WriteLine( ConfigOptions.A.ToString() ); Console.WriteLine( ConfigOptions.Both.ToString() ); // Will print: // A // A, B |
我错过了很长一段时间:可以将字符串与
1 | "string".equals("String", StringComparison.InvariantCultureIgnoreCase) |
而不是:
1 | "string".ToLower() =="String".ToLower(); |
如果方法参数实现两个接口,则可以使用泛型检查(编译时):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | interface IPropA { string PropA { get; set; } } interface IPropB { string PropB { get; set; } } class TestClass { void DoSomething<T>(T t) where T : IPropA, IPropB { MessageBox.Show(t.PropA); MessageBox.Show(t.PropB); } } |
与从基类和接口继承的参数相同。
通过
1 2 3 4 5 6 7 8 9 10 11 12 | switch (/*...*/) { case 0: // shares the exact same code as case 1 case 1: // do something goto case 2; case 2: // do something else goto default; default: // do something entirely different break; } |
我能想到的一对:
1 2 | [field: NonSerialized()] public EventHandler event SomeEvent; |
这将阻止事件序列化。"field:"表示该属性应应用于事件的支持字段。
另一个鲜为人知的功能是重写添加/删除事件处理程序:
1 2 3 4 5 6 7 8 9 10 11 12 | public event EventHandler SomeEvent { add { // ... } remove { // ... } } |
在声明类和一个或多个任意类之间执行
它似乎不是一个经常遇到的功能,但实际上它在LINQ to XML(
1 | XName tagName ="x:Name"; |
我在这篇关于如何在C中模拟多重继承的文章中发现了这个特性。
委托语法在连续的C版本中不断发展,但我仍然发现它们很难记住。幸运的是,
例如:
Action 是一个委托方法,它接受一个int参数并返回void。Func 是一种委托方法,不接受参数,返回int。Func 是一个委托方法,它接受一个int参数并返回一个bool。
这些功能是在.NET框架的3.5版中引入的。
我喜欢这样一个事实:我可以在普通的.NET 2.0上使用Linq to对象(也就是说,不需要在任何地方安装.NET 3.5)。您所需要的只是所有查询运算符扩展方法的实现—请参见LinqBridge
可以对
示例应用程序:您可以为名为
我还不能评论,但请注意,默认情况下,Visual Studio 2008会自动跳过属性,因此在这种情况下不再需要debuggersteptthrough属性。
此外,我还没有注意到有人演示如何声明一个无参数lambda(对于实现操作<>)很有用)
你还应该阅读一下闭包——我不够聪明,不能正确地解释它们。但基本上,这意味着编译器做了一些聪明的工作,这样即使在创建lambda之后,该行代码中的x超出了范围,它仍然可以工作。
我最近还发现,可以假装忽略lambda参数:
它并不是真的忽略它,它只是一个有效的标识符。所以你不能忽略这两个参数,但是我认为这是一种很好的方式来表明我们不关心这个参数(通常是eventargs,它是
要调用基类构造函数,只需将base()与构造函数内联即可。
要调用基类方法,只需将base.methodname()放在派生类方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class ClassA { public ClassA(int a) { //Do something } public void Method1() { //Do Something } } class ClassB : ClassA { public ClassB(int a) : base(a) // calling the base class constructor { //Do something } public void Method2() { base.Method1(); // calling the base class method } } |
当然,只需说
1 2 3 |
1 | x |= y |
而不是这个:
1 | x = x | y |
如果一个变量或属性(示例中的
您可以使用以下方法更改舍入方案:
1 2 3 4 5 6 7 | var value = -0.5; var value2 = 0.5; var value3 = 1.4; Console.WriteLine( Math.Round(value, MidpointRounding.AwayFromZero) ); //out: -1 Console.WriteLine(Math.Round(value2, MidpointRounding.AwayFromZero)); //out: 1 Console.WriteLine(Math.Round(value3, MidpointRounding.ToEven)); //out: 1 |
嵌套类可以访问外部类的私有成员。
1 2 3 4 5 6 7 8 9 10 11 12 | public class Outer { private int Value { get; set; } public class Inner { protected void ModifyOuterMember(Outer outer, int value) { outer.Value = value; } } } |
现在,与上面的特性一起,您也可以从嵌套类继承,就像它们是顶级类一样,如下所示。
1 2 3 4 5 6 7 | public class Cheater : Outer.Inner { protected void MakeValue5(Outer outer) { ModifyOuterMember(outer, 5); } } |
这些特性允许一些有趣的可能性,只要通过一些隐藏的类提供对特定成员的访问。
不确定是否提到了这个,但是threadstatic属性是一个真正有用的属性。这使得静态字段仅对当前线程是静态的。
1 2 | [ThreadStatic] private static int _ThreadStaticInteger; |
不应该包括初始值设定项,因为它只对整个应用程序执行一次,最好将字段设置为可以为空,并在使用之前检查该值是否为空。
ASP.NET应用程序线程还有一件事是重用的,因此如果修改该值,它最终可能会用于另一个页面请求。
不过,我发现这在很多场合都很有用。例如,在创建自定义事务类时:
1 2 3 4 5 |
DBTransaction构造函数将ThreadStatic字段设置为其自身,并在Dispose方法中将其重置为空。DoQuery检查静态字段,如果!=null使用当前事务,如果不是,则默认为其他事务。我们避免了将事务传递给每一个方法,而且它可以很容易地将其他不具有独创性的方法包装在事务内部用于事务…
只有一个用途:
很少有人知道的一件事是C引入的预处理器指令。可以使用
当我使用自顶向下的方法开发"待办事项"列表时,我通常使用这些方法。我会提醒你的。
1 | System.Diagnostics.Debug.Assert (false); |
将触发弹出窗口,并允许您在执行期间将调试器附加到正在运行的.NET进程。对于那些由于某种原因无法直接调试ASP.NET应用程序的情况非常有用。
要使用LINQ测试IEnumerabl
IEnumerable
.Any();
- 起初,我使用(IEnumerabl
.Count()!= 0)…- 它不必要地导致枚举IEnumerablEDOCX1[0]中的所有项。
- 作为对此的改进,我继续使用(IEnumerabl
.FirstOrDefault()==null)……- 哪个更好…
- 但是ienumerabl
.any()最简洁,性能最好。
使用枚举。
将字符串转换为枚举:
1 2 3 4 5 6 7 8 9 | enum MyEnum { FirstValue, SecondValue, ThirdValue } string enumValueString ="FirstValue"; MyEnum val = (MyEnum)Enum.Parse(typeof(MyEnum), enumValueString, true) |
- 我使用它从数据库的设置表中加载ASP.NET应用程序中cacheitemPriority的值,以便在不关闭应用程序的情况下动态控制缓存(以及其他设置)。
比较枚举类型的变量时,不必强制转换为int:
1 2 3 4 5 | MyEnum val = MyEnum.SecondValue; if (val < MyEnum.ThirdValue) { // Do something } |
如果希望在调试和发布模式之间有不同的行为,预处理器指令可能非常漂亮。
http://msdn.microsoft.com/en-us/library/ed8yd1ha.aspx
我非常喜欢函数上的隐式泛型参数。例如,如果您有:
1 | public void DoStuff<T>(T value); |
与其这样称呼它:
1 | DoStuff<int>(5); |
你可以:
1 | DoStuff(5); |
它将根据参数的类型计算出泛型类型。
- 如果您通过反射调用该方法,则此方法不起作用。
- 我记得Mono上有一些奇怪的问题。
串连接。这是我在这次讨论中还没有看到的。它有点模糊,但在某些情况下它是有用的。
clr保留了对文本字符串(以及以编程方式插入的字符串)的引用表。如果在代码中的多个位置使用相同的字符串,那么它将在表中存储一次。这可以减少分配字符串所需的内存量。
您可以使用string.is interned(string)测试字符串是否被截取,也可以使用string.intern(string)截取字符串。
注意:clr可以在应用程序或AppDomain端之后保存对interned字符串的引用。有关详细信息,请参阅msdn文档。
我想知道出售的
1 | int totalItems = Orders.Select(o => o.LineItems).SelectMany(i => i).Sum(); |
你可以打开绳子!
1 2 3 4 5 6 7 8 9 | switch(name) { case"Dave": return true; case"Bob": return false; default: throw new ApplicationException(); } |
非常方便!比一堆if-else语句干净得多
不是C特定的事情,但我是三元操作爱好者。
而不是
1 2 3 4 5 6 7 8 | if (boolean Condition) { //Do Function } else { //Do something else } |
你可以用简洁的
1 | booleanCondtion ? true operation : false operation; |
例如
而不是
1 2 3 4 5 6 7 8 9 | int value = param; if (doubleValue) { value *= 2; } else { value *= 3; } |
你可以打字
1 | int value = param * (tripleValue ? 3 : 2); |
它确实有助于编写简明扼要的代码,但是把那些该死的东西嵌套起来可能很讨厌,而且它们也可以用来作恶,但是我还是喜欢那些小傻瓜。
而不是这样做:
1 | Console.WriteLine("{0} item(s) found.", count); |
我使用以下内联技巧:
1 | Console.WriteLine("{0} item{1} found.", count, count==1 ?"" :"s"); |
当有一个项目时,将显示"项目";当多于(或少于)1时,将显示"项目"。一点专业性都不费吹灰之力。
C允许您向实现只读接口属性的具体类型添加属性设置器方法,即使接口声明本身没有属性设置器。例如:
1 2 3 4 | public interface IReadOnlyFoo { object SomeReadOnlyProperty { get; } } |
具体类如下:
1 2 3 4 | internal class Foo : IReadOnlyFoo { public object SomeReadOnlyProperty { get; internal set; } } |
有趣的是,如果将foo类强制转换为ireadonlyfoo接口,那么它是不可变的:
1 2 3 4 5 6 7 8 9 10 11 | // Create a Foo instance Foo foo = new Foo(); // This statement is legal foo.SomeReadOnlyProperty = 12345; // Make Foo read only IReadOnlyFoo readOnlyFoo = foo; // This statement won't compile readOnlyFoo.SomeReadOnlyProperty = 54321; |
我很确定每个人都熟悉运算符重载,但可能有些人不熟悉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class myClass { private string myClassValue =""; public myClass(string myString) { myClassValue = myString; } public override string ToString() { return myClassValue; } public static myClass operator <<(myClass mc, int shiftLen) { string newString =""; for (int i = shiftLen; i < mc.myClassValue.Length; i++) newString += mc.myClassValue[i].ToString(); mc.myClassValue = newString.ToString(); return mc; } public static myClass operator >>(myClass mc, int shiftLen) { char[] newString = new char[shiftLen + mc.myClassValue.Length]; for (int i = shiftLen; i < mc.myClassValue.Length; i++) newString[i] += mc.myClassValue[i - shiftLen]; mc.myClassValue = new string(newString); return mc; } public static myClass operator +(myClass mc, string args) { if (args.Trim().Length > 1) mc.myClassValue += args; return mc; } public static myClass operator -(myClass mc, string args) { if (args.Trim().Length > 1) { Regex rgx = new Regex(args); mc.myClassValue = rgx.Replace(mc.myClassValue,""); } return mc; } } |
我认为能够使用<<和>>左右移动字符串,或者删除一组遵循正则表达式模式的字符串,使用-=
1 2 3 4 | myClass tmpClass = new myClass(" HelloWorld123"); tmpClass -= @"World"; tmpClass <<= 2; Console.WriteLine(tmpClass); |
字典初始值设定项对于需要硬编码某些数据的快速黑客和单元测试总是有用的。
1 |
使用LINQ,可以根据参数创建新的函数。如果你有一个经常执行的小函数,那就太好了,但是参数需要一些时间来计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public Func<int> RandomGenerator { get { var r = new Random(); return () => { return r.Next(); }; } } void SomeFunction() { var result1 = RandomGenerator(); var x = RandomGenerator; var result2 = x(); } |
在C 3.5中初始化字典的表达式:
在设计视图中查看组件时要显示的属性属性:
1 2 3 4 5 6 7 8 9 | private double _Zoom = 1; [Category("View")] [Description("The Current Zoom Level")] public double Zoom { get { return _Zoom;} set { _Zoom = value;} } |
使组件库的其他用户更轻松。
我不认为有人提到过这个附录吗?值类型名称之后将使其可以为空。
你可以做到:
1 | DateTime? date = null; |
datetime是一种结构。
1 | HttpContext.Current.Server.Execute |
对于将HTML呈现为字符串进行Ajax回调非常有用。您可以将它与组件一起使用,而不是将HTML字符串片段拼凑在一起。我可以将页面膨胀减少几百kb,但实际上没有混乱。我是这样使用的:
1 2 3 4 5 6 |
object.referenceEquals方法
确定指定的对象实例是否为同一实例。
参数:
- obja:system.object-要比较的第一个对象。
- objb:系统对象-要比较的第二个对象。
例子:
1 2 3 4 5 6 7 8 | object o = null; object p = null; object q = new Object(); Console.WriteLine(Object.ReferenceEquals(o, p)); p = q; Console.WriteLine(Object.ReferenceEquals(p, q)); Console.WriteLine(Object.ReferenceEquals(o, p)); |
与"="和".等于"的差:
基本上,对象A的equals()测试与对象B具有相同的内容。
方法System.Object.ReferenceEquals()始终比较引用。尽管类可以为相等运算符提供自己的行为(如下),如果调用了该运算符,则不会调用重新定义的运算符通过对System.Object的引用。
对于字符串,实际上没有区别,因为已经重写了==和equals来比较字符串的内容。
另请参阅对另一个问题的回答("如何在不使用无限递归的"=="运算符重载中检查空值?").
高级调试显示
已经提到的属性debuggerDisplay和debuggerBrowsable控制元素的可见性和显示的文本值。简单地重写toString()将导致调试器使用该方法的输出。
如果需要更复杂的输出,可以使用/创建调试器可视化工具,例如这里有。
罢工之子微软提供了一个名为sos的调试器扩展。这是一个非常强大的扩展(尽管常常令人困惑),它是诊断"泄漏"的一种很好的方法,更准确地说,它不再需要对对象的不需要的引用。
框架源的符号服务器遵循这些说明将允许您逐步了解框架某些部分的源代码。
2010的变化Visual Studio 2010中存在一些增强功能和新功能:
- 调试并行任务
- 并行堆栈允许同时查看多个线程调用堆栈。
- 历史调试允许您及时查看事件和非局部变量(只要您提前启用了收集)。对调试方法的潜在重大更改。
除了Duncansmart的回复之外,还可以在框架2.0上使用扩展方法。只需在system.runtime.compilerservices名称空间下添加一个
1 2 3 4 5 6 | namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } |
您可以从扩展方法创建委托,就像它们是常规方法一样,使用
1 2 3 4 5 6 7 8 9 10 11 | static class FunnyExtension { public static string Double(this string str) { return str + str; } public static int Double(this int num) { return num + num; } } Func<string> aaMaker ="a".Double; Func<string, string> doubler = FunnyExtension.Double; Console.WriteLine(aaMaker()); //Prints"aa" Console.WriteLine(doubler("b")); //Prints"bb" |
注意,这对扩展值类型;请参见此问题。
1 2 | [field: NonSerialized] public event EventHandler Event; |
这样,事件侦听器就不会序列化。
只是[非序列化]不起作用,因为非序列化的属性只能应用于字段。
埃里克·利珀特的四开关怪事
键入"prop",然后按两次[tab],它会为属性生成有用的代码,并可以加快键入速度。
我知道这在VS2005中有效(我使用它),但在以前的版本中我不知道。
使用LINQ对用于获取迭代和条件的集合进行内联工作的能力是非常有价值的。值得学习的是,所有的LINQ扩展方法如何帮助您的代码更加紧凑和可维护。
C 4.0中的动态关键字
如果只希望在运行时解析方法调用,则可以使用动态关键字。
1 2 3 | dynamic invoker=new DynamicInvoker(); dynamic result1=invoker.MyMethod1(); dynamic result2=invoker.MyMethod2(); |
这里我实现了一个动态调用程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class DynamicInvoker : IDynamicObject { public MetaObject GetMetaObject (System.Linq.Expressions.Expression parameter) { return new DynamicReaderDispatch (parameter); } } public class DynamicDispatcher : MetaObject { public DynamicDispatcher (Expression parameter) : base(parameter, Restrictions.Empty){ } public override MetaObject Call(CallAction action, MetaObject[] args) { //You'll get MyMethod1 and MyMethod2 here (and what ever you call) Console.WriteLine("Logic to invoke Method '{0}'", action.Name); return this; //Return a meta object } } |
这不是特定于C的类型,但我刚刚找到了ISurogateSelector和ISerializationSurrogate接口--
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.isurogateselector.aspx
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.isurogateselector.aspx
将它们与BinaryFormatter结合使用,可以通过代理类的实现对不可序列化的对象进行序列化。代理模式在计算机科学中被很好地理解,特别是在处理序列化问题时。我认为这个实现只是作为binaryFormatter的构造函数的一个参数隐藏起来,这太糟糕了。
仍然-非常隐蔽。:)
我在读"pro asp.net mvc framework"(apress)这本书的时候,发现作者使用的字典对象与我无关。
他在不使用add()方法的情况下添加了一个新的键/值对。然后他重写了相同的键/值对,而不必检查该键是否已经存在。例如:
1 2 3 4 | Dictionary<string, int> nameAgeDict = new Dictionary<string, int>(); nameAgeDict["Joe"] = 34; // no error. will just auto-add key/value nameAgeDict["Joe"] = 41; // no error. key/value just get overwritten nameAgeDict.Add("Joe", 30); // ERROR! key already exists |
在许多情况下,我不需要检查我的字典是否已经有了一个键,我只想添加各自的键/值对(如果需要,覆盖现有的键/值对)。在这个发现之前,我总是要在添加之前检查键是否已经存在。
在通用代码中使用默认关键字返回类型的默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class GenericList<T> { private class Node { //... public Node Next; public T Data; } private Node head; //... public T GetNext() { T temp = default(T); Node current = head; if (current != null) { temp = current.Data; current = current.Next; } return temp; } } |
这里的另一个例子
当要调用/BeginInvoke内联代码时,内置(2.0)MethodInvoker委托非常有用。这就避免了需要创建一个实际的委托和单独的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 | void FileMessageEvent(object sender, MessageEventArgs e) { if (this.InvokeRequired == true) { this.BeginInvoke((MethodInvoker)delegate { lblMessage.Text=e.Message; Application.DoEvents(); } ); } } |
解决错误:"无法将匿名方法转换为类型"System.Delegate",因为它不是委托类型。"
不指定数组元素类型的数组初始化:
1 |
显式接口成员实现,其中实现了接口成员,但除非将实例强制转换为接口类型,否则将隐藏。
我喜欢
1 2 3 4 5 6 7 | #if DEBUG //Code run in debugging mode #else //Code run in release mode #endif |
这是我最近发现的一个有用的:
1 | Microsoft.VisualBasic.Logging.FileLogTraceListener |
MSDN链路
这是一个traceListener实现,它有很多特性,比如自动日志文件翻转,我以前会使用一个自定义的日志框架。好处是它是.NET的核心部分,与跟踪框架集成在一起,因此很容易立即获取和使用。
这是"隐藏的",因为它在Microsoft.VisualBasic程序集中…但是你也可以从C使用它。
关于
不确定是否提到过这个(11页!!)
但是,当您对要序列化的类/对象进行版本控制时,类的
http://msdn.microsoft.com/en-us/library/ms229752(vs.80).aspx
在非泛型类中可以有泛型方法。
使用lambda时模拟函数"通配符"参数(如haskell中的"uu")的绝妙技巧:
1 | (_, b, __) => b.DoStuff(); // only interested in b here |
这不是一个C特定的功能,但它是一个插件,我觉得非常有用。它被称为资源重构工具。它允许您右键单击文本字符串并将其提取到资源文件中。它将搜索代码并找到匹配的任何其他文本字符串,并用resx文件中的相同资源替换它。
http://www.codeplex.com/resourcerefactoring
方法组并不为人所知。
鉴于:
1 2 | Func<Func<int,int>,int,int> myFunc1 = (i, j) => i(j); Func<int, int> myFunc2 = i => i + 2; |
您可以这样做:
1 | var x = myFunc1(myFunc2, 1); |
而不是这个:
1 | var x = myFunc1(z => myFunc2(z), 1); |
我之所以称之为autodebug,是因为您可以根据bool值直接进入debug,该值也可以存储为项目用户设置。
例子:
1 2 3 4 5 6 | //Place at top of your code public UseAutoDebug = true; //Place anywhere in your code including catch areas in try/catch blocks Debug.Assert(!this.UseAutoDebug); |
只需将上述内容放在try/catch块或代码的其他区域中,并将useautodebug设置为true或false,然后在希望测试的任何时候进行调试。
您可以将此代码保留在适当的位置,并在测试时打开和关闭此功能,还可以将其保存为项目设置,并在部署后手动更改它,以便在需要时/如果需要时从用户那里获取更多的错误信息。
在这个Visual Studio C项目模板中,您可以看到使用此技术的功能性和工作性示例,其中它被大量使用:
http://code.msdn.microsoft.com/sehe
当yield关键字具有很大的威力时,它常常被忽略。我不久前在博客上讨论过它,讨论了它的好处(不同的处理方式),并且在屈服的情况下发生,以帮助提供更深入的理解。
使用C中的产量#
新修饰语
C中"new"修饰符的用法并不完全隐藏,但并不常见。当需要"隐藏"基类成员而不总是重写它们时,新的修饰符就派上用场了。这意味着当您将派生类强制转换为基类时,"hidden"方法将变为可见的,并在派生类中调用,而不是使用相同的方法。
在代码中更容易看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class BaseFoo { virtual public void DoSomething() { Console.WriteLine("Foo"); } } public class DerivedFoo : BaseFoo { public new void DoSomething() { Console.WriteLine("Bar"); } } public class DerivedBar : BaseFoo { public override void DoSomething() { Console.WriteLine("FooBar"); } } class Program { static void Main(string[] args) { BaseFoo derivedBarAsBaseFoo = new DerivedBar(); BaseFoo derivedFooAsBaseFoo = new DerivedFoo(); DerivedFoo derivedFoo = new DerivedFoo(); derivedFooAsBaseFoo.DoSomething(); //Prints"Foo" when you might expect"Bar" derivedBarAsBaseFoo.DoSomething(); //Prints"FooBar" derivedFoo.DoSomething(); //Prints"Bar" } } |
[艾德:双关语我能得到额外的分数吗?很抱歉,无法帮助。]
读完这9页后,我觉得我必须指出一个不太知名的特点…
对于.NET 1.1来说,这是正确的,在gzip文件上使用压缩/解压缩,必须:
- 下载icsharpcode.ziplib
- 或者,将Java库引用到项目中,并使用Java内置的库来利用GZIP的压缩/解压缩方法。
它使用不足,我不知道(仍然使用icsharpcode.ziplib,即使使用.NET 2/3.5),它被合并到标准BCL版本2以上的system.io.compression命名空间中…请参阅msdn页面"gzipstream class"。
我发现在Visual Studio中使用条件中断函数非常有用。我喜欢它允许我将值设置为某些东西的方式,例如,只有在很少的情况下才能满足,从那里我可以进一步检查代码。
从匿名方法访问局部变量允许您使用新的控制流逻辑包装任何代码,而不必将该代码分解为另一个方法。方法外部声明的局部变量在方法内部可用,例如示例中的
Retrying on exception conditionally.
文本可以用作该类型的变量。如。
1 2 3 | Console.WriteLine(5.ToString()); Console.WriteLine(5M.GetType()); // Returns"System.Decimal" Console.WriteLine("This is a string!!!".Replace("!!","!")); |
只是一些琐事…
有很多事情人们没有提到,但他们大多与不安全的结构有关。下面是一个可以被"常规"代码使用的代码:
选中/未选中的关键字:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static int UncheckedAddition(int a, int b) { unchecked { return a + b; } } public static int CheckedAddition(int a, int b) { checked { return a + b; } // or"return checked(a + b)"; } public static void Main() { Console.WriteLine("Unchecked:" + UncheckedAddition(Int32.MaxValue, + 1)); //"Wraps around" Console.WriteLine("Checked:" + CheckedAddition(Int32.MaxValue, + 1)); // Throws an Overflow exception Console.ReadLine(); } |
(我刚用过这个)设置一个字段空值,然后返回它,不带中间变量:
1 2 3 4 5 6 7 8 | try { return _field; } finally { _field = null; } |
@ Brad Barker
我认为如果必须使用可以为空的类型,最好使用可以为空的<.t>而不是问号表示法。很明显,魔法正在发生。不知道为什么会有人想要使用nullable<.bool>。-)
Krzysztof cwalina(Framwork Design Guidlines的作者之一)在这里有一篇好文章:http://blogs.msdn.com/kcwalina/archive/2008/07/16/nullable.aspx
麦克·哈德罗在零位巫术上有个不错的位置
我觉得难以置信的是,编译器在使用外部变量时遇到了什么样的麻烦:
1 2 3 4 | string output ="helo world!"; Action action = () => Console.WriteLine(output); output ="hello!"; action(); |
这实际上打印了
1 2 3 4 5 6 7 |
我觉得很酷。
我不知道
1 2 3 4 | int[] someArrayYouHaveAsInt; double[] copyOfArrayAsDouble = Array.ConvertAll<int, double>( someArrayYouHaveAsInt, new Converter<int,double>(Convert.ToDouble)); |
这样就避免了定义内联委托/闭包(并且可读性稍高)所引起的资源分配问题:
1 2 3 4 5 6 | int[] someArrayYouHaveAsInt; double[] copyOfArrayAsDouble = Array.ConvertAll<int, double>( someArrayYouHaveAsInt, new Converter<int,double>( delegate(int i) { return (double)i; } )); |
混音是一个很好的功能。基本上,mixins让您拥有接口的具体代码,而不是类。然后,只需在一组类中实现接口,就可以自动获得混合功能。例如,要将深度复制混合到多个类中,请定义一个接口
1 | internal interface IPrototype<T> { } |
为此接口添加功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | internal static class Prototype { public static T DeepCopy<T>(this IPrototype<T> target) { T copy; using (var stream = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, (T)target); stream.Seek(0, SeekOrigin.Begin); copy = (T) formatter.Deserialize(stream); stream.Close(); } return copy; } } |
然后在任何类型中实现接口以获得混合。
C中的指针。
它们可以用来执行就地字符串操作。这是一个不安全的功能,因此不安全关键字用于标记不安全代码的区域。还要注意fixed关键字是如何用于指示指向的内存是固定的,并且不能被gc移动。这是必需的,指针指向内存地址,GC可以将内存移动到不同的地址,否则会导致指针无效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | string str ="some string"; Console.WriteLine(str); unsafe { fixed(char *s = str) { char *c = s; while(*c != '\0') { *c = Char.ToUpper(*c++); } } } Console.WriteLine(str); |
我不会这么做,只是为了这个问题来演示这个特性。
这意味着t必须有一个公共的无参数构造函数:
1 2 3 4 |
无特殊顺序:
1 2 | Lists<> Mutex |
框架3.5中的新属性定义快捷方式。
可以为枚举定义数据类型:
1 2 3 4 5 | enum EnumName : [byte, char, int16, int32, int64, uint16, uint32, uint64] { A = 1, B = 2 } |
刚刚学习了不变性、协方差和反方差的含义,我发现了将包含在.NET4.0中的输入和输出泛型修饰符。它们看起来足够模糊,以至于大多数程序员都不知道它们。
在Visual Studio杂志上有一篇文章讨论了这些关键字以及它们将如何使用。
开放泛型是另一个方便的特性,特别是在使用控制反转时:
在阅读有关.NET框架开发的书时。一个好的建议是不要使用bool来打开或关闭东西,而是使用枚举。
使用Enums,您可以给自己一些可扩展性,而不必重写任何代码来向函数添加新功能。
与使用int.typarse()或convert.toint32()不同,我喜欢使用一个静态整数解析函数,该函数在无法解析时返回空值。那我可以用吗??和三元运算符一起更清楚地确保我的声明和初始化都以一种易于理解的方式在一行上完成。
1 2 3 4 5 6 7 8 9 | public static class Parser { public static int? ParseInt(string s) { int result; bool parsed = int.TryParse(s, out result); if (parsed) return result; else return null; } // ... } |
这也有助于避免复制赋值的左侧,但更好的方法是避免在赋值的右侧复制长调用,如下面的示例中的数据库调用。而不是丑陋的如果那么树(我经常碰到):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | int x = 0; YourDatabaseResultSet data = new YourDatabaseResultSet(); if (cond1) if (int.TryParse(x_input, x)){ data = YourDatabaseAccessMethod("my_proc_name", 2, x); } else{ x = -1; // do something to report"Can't Parse" } } else { x = y; data = YourDatabaseAccessMethod("my_proc_name", new SqlParameter("@param1", 2), new SqlParameter("@param2", x)); } |
你可以做到:
1 2 3 4 |
更干净更容易理解
这个问题我来的太晚了,但我想补充一些我认为还没有被涵盖的内容。这些并不是特定于C的,但我认为它们对于任何C开发人员都是值得一提的。
环境值属性这与
使用独立存储设置这是一个Silverlight。框架轻而易举地包含了这个密封类,用于在每个应用程序和每个站点级别提供设置持久性。
标记与扩展方法的交互使用扩展方法,标记枚举的使用可以更可读。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static bool Contains( this MyEnumType enumValue, MyEnumType flagValue) { return ((enumValue & flagValue) == flagValue); } public static bool ContainsAny( this MyEnumType enumValue, MyEnumType flagValue) { return ((enumValue & flagValue) > 0); } |
这使得对标志值的检查很好,并且易于读写。当然,如果我们可以使用泛型并强制t成为枚举,那就更好了,但这是不允许的。也许
考虑过@dp anoncast,决定尝试一下。以下是我提出的可能对某些人有用的建议:
1 2 3 4 5 | // using the concepts of dp's AnonCast static Func<T> TypeCurry<T>(Func<object> f, T type) { return () => (T)f(); } |
它的使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | static void Main(string[] args) { var getRandomObjectX = TypeCurry(GetRandomObject, new { Name = default(string), Badges = default(int) }); do { var obj = getRandomObjectX(); Console.WriteLine("Name : {0} Badges : {1}", obj.Name, obj.Badges); } while (Console.ReadKey().Key != ConsoleKey.Escape); } static Random r = new Random(); static object GetRandomObject() { return new { Name = Guid.NewGuid().ToString().Substring(0, 4), Badges = r.Next(0, 100) }; } |
我特别喜欢可以为空的日期时间。因此,如果您有一些情况下给出了日期,而其他情况下没有给出日期,我认为这是最好使用的,而且我更容易理解为使用
1 2 3 4 5 6 7 8 9 10 | DateTime? myDate = null; if (myDate.HasValue) { //doSomething } else { //soSomethingElse } |
Math.Max和Min检查边界:我在很多代码中看到过:
1 2 3 4 | if (x < lowerBoundary) { x = lowerBoundary; } |
我发现这个更小、更干净、更可读:
1 | x = Math.Max(x, lowerBoundary); |
也可以使用三元运算符:
1 | x = ( x < lowerBoundary) ? lowerBoundary : x; |
@莱因,
当从可以为空的数据库中检索值并将值放回时,可以为空的布尔值非常有用。有时你想知道字段还没有设置。
为了便于阅读,我喜欢使用using指令重命名一些类:
1 2 3 4 5 6 7 8 9 10 11 | // defines a descriptive name for a complexed data type using MyDomainClassList = System.Collections.Generic.List< MyProjectNameSpace.MyDomainClass>; .... MyDomainClassList myList = new MyDomainClassList(); /* instead of List<MyDomainClass> myList = new List<MyDomainClass>(); */ |
这对于代码维护也非常方便。如果您需要更改类名,那么只有一个地方需要更改。另一个例子:
1 2 3 4 5 | using FloatValue = float; // you only need to change it once to decimal, double... .... FloatValue val1; ... |
零参数lambdas
1 | ()=>Console.ReadLine() |
我没看到:
1 | for (;;); |
一样
1 | while (true) ; |
system.runtime.remoting.proxys.realproxy
它可以在C中实现面向方面的编程,而且您也可以用它做很多其他花哨的事情。
我喜欢editorbrowableattribute。它允许您控制是否在IntelliSense中显示方法/属性。可以将值设置为"始终"、"高级"或"从不"。
从MSDN…
评论
editorbrowableattribute是对设计器的提示,指示是否显示属性或方法。可以在可视化设计器或文本编辑器中使用此类型来确定用户可见的内容。例如,Visual Studio中的IntelliSense引擎使用此属性确定是否显示属性或方法。
在Visual C_中,您可以控制高级属性何时出现在IntelliSense中,以及属性窗口中的工具选项文本编辑器C_下的隐藏高级成员设置。相应的editorBrowsableState为高级。
如果有人提到这个我很抱歉,但我经常使用这个。
AlexPapadimoulis为Visual Studio开发了一个插件。它用于将常规文本粘贴为字符串、字符串生成器、注释或区域。
http://weblogs.asp.net/alex_papadimoulis/archive/2004/05/25/smart-paster-1.1-加载项---stringbuilder-and-better-c_2300-handling.aspx
在这个插件中(我也不知道是否提到过这个),我注意到字符串是用字符串文字前缀粘贴的:
1 | @ |
我知道这些,但我不知道如何在文本中使用双引号来转义引号。
例如
1 | string s ="A line of text" + Environment.NewLine +"Another with a "quote"!!"; |
可以表示为
1 2 | string s = @"A line of text Another with a""quote""!!"; |
_还有arglist
1 2 3 4 5 6 7 8 | [DllImport("msvcrt40.dll")] public static extern int printf(string format, __arglist); static void Main(string[] args) { printf("Hello %s! ", __arglist("Bart")); } |
action和func与lambda方法一起委托助手。我将这些用于需要委托以提高可读性的简单模式。例如,一个简单的缓存模式是检查请求的对象是否存在于缓存中。如果它确实存在:返回缓存的对象。如果不存在,则生成新实例,缓存新实例并返回新实例。相反,我可以为我可能从缓存中存储/检索的每个对象编写1000次代码,这样我就可以编写一个简单的模式方法……
1 2 3 4 5 6 7 8 9 | private static T CachePattern<T>(string key, Func<T> create) where T : class { if (cache[key] == null) { cache.Add(key, create()); } return cache[key] as T; } |
…然后,我可以在自定义缓存管理器中使用以下内容,从而大大简化缓存获取/设置代码
1 2 3 4 5 6 7 | public static IUser CurrentUser { get { return CachePattern<IUser>("CurrentUserKey", () => repository.NewUpUser()); } } |
现在,简单的"日常"代码模式可以编写一次,并且可以更容易地重用imho。我不必去编写委托类型并弄清楚我要如何实现回调等。如果我能在10秒钟内编写它,我就不太倾向于使用剪切/粘贴简单的代码模式,不管它们是延迟初始化还是上面显示的其他一些示例…
反射发出,表达树浮现在脑海中…
不要错过杰弗里·里克特通过C和乔恩·斯基特的的clr。
有关某些资源,请参阅此处:
http://www.codeproject.com/kb/trace/releasemodebreakpoint.aspx
http://www.codeproject.com/kb/dotnet/creating_dynamic_types.aspx
http://www.codeproject.com/kb/cs/lambdaexpressions.aspx
这不会编译:
1 2 3 4 5 6 7 | namespace ns { class Class1 { Nullable<int> a; } } |
找不到类型或命名空间名称"nullable"(是否缺少using指令或程序集引用?)<--缺少'EDOCX1[5]
但是
1 2 3 4 5 6 7 | namespace ns { class Class1 { int? a; } } |
将编译!(.NET 2)。
在使用LinqXML时,我发现这种技术很有趣:
1 2 3 | public bool GetFooSetting(XElement ndef){ return (bool?)ndef.Element("MyBoolSettingValue") ?? true; } |
与之相反:
1 2 3 | public bool GetFooSetting(XElement ndef){ return ndef.Element("MyBoolSettingValue") != null ? bool.Parse(ndef.Element("MyBoolSettingValue") ) : true; } |
通用事件处理程序:
1 | public event EventHandler<MyEventArgs> MyEvent; |
这样你就不必一直声明你自己的代表,
我几乎有一年没有发现强类型数据行包含一个is[columnname]null()方法。
例如:
1 2 3 4 5 6 7 8 9 10 | Units.UnitsDataTable dataTable = new Units.UnitsDataTable(); foreach (Units.UnitsRow row in dataTable.Rows) { if (row.IsPrimaryKeyNull()) //.... if (row.IsForeignKeyNull()) //.... } |
可以为空。是否获取值或默认值?
FIXED / Power of Pointers in C# - This topic is too big, but I will just outline simple things.
在C中,我们有装载结构的设施,比如…
1 2 3 4 5 6 7 8 9 | struct cType{ char type[4]; int size; char name[50]; char email[100]; } cType myType; fread(file, &mType, sizeof(mType)); |
我们可以在"不安全"方法中使用fixed关键字来读取字节数组对齐的结构。
1 2 3 4 5 6 7 | [Layout(LayoutKind.Sequential, Pack=1)] public unsafe class CType{ public fixed byte type[4]; public int size; public fixed byte name[50]; public fixed byte email[100]; } |
方法1(从规则流读取到字节缓冲区,并将字节数组映射到结构的各个字节)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CType mType = new CType(); byte[] buffer = new byte[Marshal.SizeOf(CType)]; stream.Read(buffer,0,buffer.Length); // you can map your buffer back to your struct... fixed(CType* sp = &mType) { byte* bsp = (byte*) sp; fixed(byte* bp = &buffer) { for(int i=0;i<buffer.Length;i++) { (*bsp) = (*bp); bsp++;bp++; } } } |
方法2,可以将win32 user32.dll的readfile映射为直接读取字节…
1 2 3 4 5 |
我最近刚学到的一点是,您仍然可以对一个可以为空的值调用方法….
当你有一个可以为空的值时,结果是:
1 | decimal? MyValue = null; |
你可能认为你必须写:
1 | MyValue == null ? null : MyValue .ToString() |
你可以写:
1 | MyValue.ToString() |
我知道我可以调用myValue.hasValue和myValue.Value…但是它没有完全单击我可以调用toString()。
是否已引用构造函数链?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | namespace constructorChain { using System; public class Class1 { public string x; public string y; public Class1() { x ="class1"; y =""; } public Class1(string y) : this() { this.y = y; } } public class Class2 : Class1 { public Class2(int y) : base(y.ToString()) { } } } |
…
1 2 3 4 5 6 7 8 | constructorChain.Class1 c1 = new constructorChain.Class1(); constructorChain.Class1 c12 = new constructorChain.Class1("Hello, Constructor!"); constructorChain.Class2 c2 = new constructorChain.Class2(10); Console.WriteLine("{0}:{1}", c1.x, c1.y); Console.WriteLine("{0}:{1}", c12.x, c12.y); Console.WriteLine("{0}:{1}", c2.x, c2.y); Console.ReadLine(); |
允许使用带大括号的空块。
你可以这样写代码
1 2 3 | { service.DoTonsOfWork(args); } |
如果你想尝试一些没有你已经写过的
1 2 3 4 | //using(var scope = new TransactionScope) { service.DoTonsOfWork(args); } |
我学到的一件有趣的事情是,框架和C语言的不同部分是在不同的时间编写的,因此不一致。例如,框架本身违反了许多fxcop规则,因为在编写框架时,这些规则并没有全部到位。
此外,using语句旨在取消"范围"的定义,而不是专门用于处理资源。它是在lock语句之后编写的。EricGunnerson曾经提到过这样的事情:如果using语句排在第一位,那么他们可能不需要编写lock语句(尽管谁知道呢,也许他们会有任何方式),因为using语句可能已经足够了。
我喜欢滥用静态模板类不共享其静态成员的事实。
这里有一个threadsafe(在创建时)和廉价的替代品,当
1 2 3 4 5 6 | public static class MyCachedData<T>{ static readonly CachedData Value; static MyCachedData(){ Value=// Heavy computation, such as baking IL code or doing lots of reflection on a type } } |
干杯,弗洛里安
将枚举值转换为字符串值
给定枚举
1 2 3 4 5 6 | enum Country { UnitedKingdom, UnitedStates, UnitedArabEmirates, } |
使用它:
1 2 3 4 | public static void PrintEnumAsString( Country country ) { Console.Writeline( country.ToString() ); } |
将枚举值的名称打印为字符串,例如"UnitedKingdom"
- 附着?到一种类型可以为空,例如:int?
- "C:dir"而不是@"C:dir"
我只想提一下(因为在t:struct中的运算方式),C编译器的其中一个功能就是
1 | where T : Enum |
不会编译。它抛出错误"constraint不能是特殊类"System.Enum""。
我在这次谈话中有点晚了,我想贡献一下。对于一些开发人员来说,这可能是一件新鲜事。
1 2 3 4 5 6 7 | public class User { public long UserId { get; set; } public String Name { get; set; } public String Password { get; set; } public String Email { get; set; } } |
声明和初始化它的通常方法是使用构造函数或类似的方法。
1 2 3 4 |
但我学会了用以下方法初始化它。我知道VisualBasic开发人员会喜欢它,因为它就像是只在vb.net中提供的运算符,而不是在C中一样,如下所示。
1 2 3 4 5 6 7 | User user = new User() { UserId = 1, Name ="myname", Email ="[email protected]", Password ="mypassword" }; |
Visual Studio最有用的功能之一是"生成对象ID"。它生成一个ID并"附加"到对象上,因此无论您在何处查看对象,您都将看到该ID(与线程无关)。
调试时,右键单击变量工具提示,就可以看到它了。它也适用于监视/自动/局部变量。
这与静态构造函数有关。这是一种执行静态销毁的方法(即在程序退出时清理资源)。
先下课:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class StaticDestructor { /// <summary> /// The delegate that is invoked when the destructor is called. /// </summary> public delegate void Handler(); private Handler doDestroy; /// <summary> /// Creates a new static destructor with the specified delegate to handle the destruction. /// </summary> /// <param name="method">The delegate that will handle destruction.</param> public StaticDestructor(Handler method) { doDestroy = method; } ~StaticDestructor() { doDestroy(); } } |
然后作为类的成员,您希望有一个"静态析构函数"做:
1 2 3 4 5 6 7 | private static readonly StaticDestructor destructor = new StaticDestructor ( delegate() { //Cleanup here } ); |
当最终垃圾收集发生时,现在将调用此函数。如果您绝对需要释放某些资源,这很有用。
表现出这种行为的快速而肮脏的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | using System; namespace TestStaticDestructor { class StaticDestructor { public delegate void Handler(); private Handler doDestroy; public StaticDestructor(Handler method) { doDestroy = method; } ~StaticDestructor() { doDestroy(); } } class SomeClass { static SomeClass() { Console.WriteLine("Statically constructed!"); } static readonly StaticDestructor destructor = new StaticDestructor( delegate() { Console.WriteLine("Statically destructed!"); } ); } class Program { static void Main(string[] args) { SomeClass someClass = new SomeClass(); someClass = null; System.Threading.Thread.Sleep(1000); } } } |
当程序退出时,调用"静态析构函数"。
我不纵容,但我很惊讶Goto仍然在鸭子周围来袭的炮弹
对象初始值设定项内的集合初始值设定项:
1 2 3 4 | MailMessage mail = new MailMessage { To = { new MailAddress("[email protected]"), new MailAddress("[email protected]") }, Subject ="Password Recovery" }; |
可以在单个表达式中初始化整个树。
刚刚学会了EDOCX1 0的乐趣,尝试与一个非托管C++函数库接口,它定义了没有AytStDelk的回调函数。
我想很多人知道C语言中的指针,但不确定它是否在C语言中起作用。您可以在不安全的上下文中使用c中的指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static void Main() { int i; unsafe { // pointer pi has the address of variable i int* pi = &i; // pointer ppi has the address of variable pi int** ppi = π // ppi(addess of pi) -> pi(addess of i) -> i(0) i = 0; // dereference the pi, i.e. *pi is i Console.WriteLine("i = {0}", *pi); // output: i = 0 // since *pi is i, equivalent to i++ (*pi)++; Console.WriteLine("i = {0}", *pi); // output: i = 1 // since *ppi is pi, one more dereference *pi is i // equivalent to i += 2 **ppi += 2; Console.WriteLine("i = {0}", *pi);// output: i = 3 } Console.ReadLine(); } |
从我这里来一些-你想怎么做就怎么做。
属性:
1 | [assembly::InternalsVisibleTo("SomeAssembly")] |
允许您将内部方法/属性或数据从程序集公开到另一个名为"someassembly"的程序集。所有受保护/私人物品都将被隐藏。
静态构造函数(也称为"类型构造函数")。
1 2 3 4 5 6 7 8 | public MyClass { public static MyClass() { // type init goes here } ...... } |
关键字
如果在Visual Studio工具栏中有搜索文本框,则可以键入">of program.cs"以打开文件program.cs。
region string和_endregion对非常适合分组代码(大纲显示)。
1 2 3 4 5 6 | #region Using statements using System; using System.IO; using ....; using ....; #endregion |
代码块可以压缩为一行描述的文本。也可以在函数内部工作。
不可序列化的营销事件:
1 2 | [field:NonSerializable] public event SomeDelegate SomeEvent; |
一般约束:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //Constructor constraint, T has a default empty constructor class Node<K,T> where T : new() { } //Reference\Value Type constraints //T is a struct public class MyClass<T> where T : struct {...} //T is a reference type public class MyClass<T> where T : class {...} public class MyClass<T> where T : SomeBaseClass, ISomeInterface {...} |
对于数据库应用程序开发人员来说,这可能是非常基本的,但我花了一段时间才意识到null与dbnull.value不同。
如果要查看数据库记录中的值是否为空,则必须使用dbnull.value。
能够基于这样的泛型参数创建类型的实例
新的T-();
表情树怎么样?他们是Linq的核心,允许延期执行:
摘自大卫·海登的博客:
在C 3.0中,可以使用lambda表达式如下定义委托:
1 | Func<int,int> f = x => x + 1; |
此委托被编译为应用程序中的可执行代码,可以这样调用:
1 | var three = f(2); // 2 + 1 |
代码按预期工作。这里没什么好奇心。
表达式树
使用System.Query.Expression将委托定义为表达式树时:
1 | Expression<Func<int,int>> expression = x => x + 1; |
委托不再编译为可执行代码,而是编译为可以转换并编译为原始委托的数据。
要在应用程序中实际使用表示为表达式树的委托,必须在应用程序中编译和调用它:
1 2 3 | var originalDelegate = expression.Compile(); var three = originalDelegate.Invoke(2); |
框架功能
我不知道,但我对visualstylenderer和整个system.windows.forms.visualstyles-namespace非常惊讶。相当酷!
标准中已经解释了很多这方面的内容。对于初学者和专家来说,这是一个很好的阅读方式,阅读量很大,但这是官方标准,而且其中充满了丰富的细节。
一旦您完全理解了C,是时候进一步了解公共语言基础结构的基础知识了。C的架构和基础。
我遇到过很多程序员,除了他们坚持的限制外,他们不知道对象和值类型之间的区别。
熟悉这两个文件,你就永远不会成为那个人。
验证用户输入时,每个基元类型的台盼色方法都很好。
1 2 3 4 5 | double doubleValue if (!Double.TryParse(myDataRow("myColumn"), out doubleValue)) { // set validation error } |
InternalsVisibleToAttribute指定通常仅在当前程序集中可见的类型对其他程序集可见。关于MSDN的文章
您可以将
泛型和奇怪的重复模板模式确实有助于某些静态方法/属性声明。
假设您正在构建一个类层次结构:
1 2 3 4 5 6 7 8 9 10 11 | class Base { } class Foo: Base { } class Bar: Base { } |
现在,您要在类型上声明静态方法,这些方法应采用相同类型的参数(或返回值)或相同类型的静态属性。例如,您需要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Base { public static Base Get() { // Return a suitable Base. } } class Foo: Base { public static Foo Get() { // Return a suitable Foo. } } class Bar: Base { public static Bar Get() { // Return a suitable Bar. } } |
如果这些静态方法基本上都做相同的事情,那么您手上就有很多重复的代码。一种解决方案是删除返回值的类型安全性,并始终返回类型
1 2 3 4 5 6 7 | class Base<T> where T: Base<T> { public static T Get<T>() { // Return a suitable T. } } |
你和你分别是:
1 2 3 4 5 6 7 | class Foo: Base<Foo> { } class Bar: Base<Bar> { } |
这样,它们将自动获得静态方法的副本。
在基类中封装singleton模式也是一个奇迹(我知道下面的代码不是线程安全的,只是为了演示一点):
1 2 3 4 5 6 7 8 9 |
我认识到这迫使您在单例子类上拥有一个公共的无参数构造函数,但是在编译时没有
最近我了解了string.join方法。当构建像列这样的字符串以供查询选择时,它非常有用。
仅用于引用-使用扩展方法枚举二进制操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; namespace BinaryOpGenericTest { [Flags] enum MyFlags { A = 1, B = 2, C = 4 } static class EnumExtensions { private static Dictionary<Type, Delegate> m_operations = new Dictionary<Type, Delegate>(); public static bool IsFlagSet<T>(this T firstOperand, T secondOperand) where T : struct { Type enumType = typeof(T); if (!enumType.IsEnum) { throw new InvalidOperationException("Enum type parameter required"); } Delegate funcImplementorBase = null; m_operations.TryGetValue(enumType, out funcImplementorBase); Func<T, T, bool> funcImplementor = funcImplementorBase as Func<T, T, bool>; if (funcImplementor == null) { funcImplementor = buildFuncImplementor(secondOperand); } return funcImplementor(firstOperand, secondOperand); } private static Func<T, T, bool> buildFuncImplementor<T>(T val) where T : struct { var first = Expression.Parameter(val.GetType(),"first"); var second = Expression.Parameter(val.GetType(),"second"); Expression convertSecondExpresion = Expression.Convert(second, typeof(int)); var andOperator = Expression.Lambda<Func<T, T, bool>>(Expression.Equal( Expression.And( Expression.Convert(first, typeof(int)), convertSecondExpresion), convertSecondExpresion), new[] { first, second }); Func<T, T, bool> andOperatorFunc = andOperator.Compile(); m_operations[typeof(T)] = andOperatorFunc; return andOperatorFunc; } } class Program { static void Main(string[] args) { MyFlags flag = MyFlags.A | MyFlags.B; Console.WriteLine(flag.IsFlagSet(MyFlags.A)); Console.WriteLine(EnumExtensions.IsFlagSet(flag, MyFlags.C)); Console.ReadLine(); } } } |
不知道这本身是否是一个秘密,但我喜欢System.Linq中添加的Enumerable(添加到IEnumerable)类。
http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx
而yield关键字已列出。迭代器块是惊人的。我用它们来构建列表,这些列表将被测试,以查看它们是否是共同素数。它基本上允许您遍历一个逐个返回值并随时停止的函数。
哦,当你不能再优化它的时候,我差点忘了世界上最好的班级。幕后工作者!!!!!
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
当与.NET 3.5中的语句lambdas一起使用时,肯定是func<>类型。这些允许自定义函数,并且可以帮助用户提供自定义对象,而无需对其进行子类化,也不必求助于某些有限的系统,例如跟踪列出用户要监视的按钮或键的变量。而且,它们可以像常规方法一样被调用,也可以像变量一样被赋值。我能想到的唯一缺点是你只能有5个论点!尽管到那时你可能想考虑一个不同的解决方案…编辑:提供一些例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ... public Func<InputHelper, float> _horizontalCameraMovement = (InputHelper input) => { return (input.LeftStickPosition.X * _moveRate) * _zoom; } public Func<InputHelper, float> _verticalCameraMovement = (InputHelper input) => { return (-input.LeftStickPosition.Y * _moveRate) * _zoom; } ... public void Update(InputHelper input) { ... position += new Vector2(_horizontalCameraMovement(input), _verticalCameraMovement(input)); ... } |
在本例中,您可以编写一个执行任意计算的函数,并返回一个浮动,该浮动将确定相机的移动量。不是最好的代码,但它得到了重点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private int foo; public int FooProperty { get { if (_onFooGotten() == true) return _foo; } set { if (onFooSet() == true) _foo = value; } } ... public Func<bool> _onFooGotten = () => { //do whatever... return true; } public Func<bool> _onFooSet = () => { //do whatever... return true; } |
这不是最好的例子(因为我还没有真正探讨过这种用法),但它展示了一个使用lambda函数进行快速事件提升的例子,而不需要委托人的麻烦。编辑:想到另一个。零号!C最接近的参数是可选参数。
上下文绑定对象
与其说是一个C东西,不如说是一个.NET东西。这是实现DI的另一种方法,尽管它可能很努力。你必须从中继承,这是不可推迟的。
http://msdn.microsoft.com/en-us/library/system.contextboundobject.aspx
当我用一个定制的日志属性修饰一个类/方法时,我使用它来添加日志记录。
threadstaticattribute是我最喜欢的。此外,非序列化属性也很有用。(你能告诉我使用远程处理做了很多服务器工作吗?)
好。。。不要使用它,但很多人不知道C支持邪恶的Goto:)
1 2 3 4 5 6 7 8 9 10 11 | static void Example() { int i = 0; top: Console.WriteLine(i.ToString()); if (i == 0) { i++; goto top; } } |
viewstate getter可以是一行程序。
使用默认值:
1 2 3 4 5 6 7 8 9 10 11 | public string Caption { get { return (string) (ViewState["Caption"] ??"Foo"); } set { ViewState["Caption"] = value; } } public int Index { get { return (int) (ViewState["Index"] ?? 0); } set { ViewState["Index"] = value; } } |
使用空值作为默认值:
1 2 3 4 5 6 7 8 9 10 11 | public string Caption { get { return (string) ViewState["Caption"]; } set { ViewState["Caption"] = value; } } public int? Index { get { return (int?) ViewState["Index"]; } set { ViewState["Index"] = value; } } |
这适用于任何有字典支持的东西。
返回IQueryable投影
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | protected void LdsPostings_Selecting(object sender, LinqDataSourceSelectEventArgs e) { var dc = new MyDataContext(); var query = dc.Posting.AsQueryable(); if (isCondition1) { query = query.Where(q => q.PostedBy == Username); e.Result = QueryProjection(query); return; } ... if (isConditionN) { query = query.Where(q => q.Status.StatusName =="submitted"); query = query.Where(q => q.ReviewedBy == Username); e.Result = QueryProjection(query); return; } } |
创建一个方法,而不是对投影进行多次编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private IQueryable QueryProjection(IQueryable<Posting> query) { return query.Select(p => new { p.PostingID, p.Category.CategoryName, p.Type.TypeName, p.Status.StatusName, p.Description, p.Updated, p.PostedBy, p.ReviewedBy, }); } |
关于post w/perma链接"C的隐藏特性"?有另一种方法来完成相同的操作-缩进/换行。看看这个……
1 2 3 4 5 6 7 8 | XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); xmlWriterSettings.NewLineOnAttributes = true; xmlWriterSettings.Indent = true; XmlWriter xml = XmlWriter.Create(@"C:\file.xml", xmlWriterSettings); // Start writing the data using xml.WriteStartElement(), xml.WriteElementString(...), xml.WriteEndElement() etc |
但我不确定这是否是一个未知的功能!
1 | double dSqrd = Math.Pow(d,2.0); |
比
1 | double dSqrd = d * d; // Here we can lose precision |
下面的一个不是隐藏的,但它是相当隐式的。我不知道下面的示例是否已经在这里发布了,我看不出有什么好处(可能没有),但我会尝试显示一个"奇怪"的代码。下面的示例通过c(delegates/anonymous delegates[lambdas])和闭包中的函数模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | private static readonly Action EmptyAction = () => { }; private static readonly Func<bool> EmptyCondition = () => { return true; }; private sealed class BreakStatementException : Exception { } private sealed class ContinueStatementException : Exception { } private static void Break() { throw new BreakStatementException(); } private static void Continue() { throw new ContinueStatementException(); } private static void For(Action init, Func<bool> condition, Action postBlock, Action statement) { init = init ?? EmptyAction; condition = condition ?? EmptyCondition; postBlock = postBlock ?? EmptyAction; statement = statement ?? EmptyAction; for ( init(); condition(); postBlock() ) { try { statement(); } catch ( BreakStatementException ) { break; } catch ( ContinueStatementException ) { continue; } } } private static void Main() { int i = 0; // avoiding error"Use of unassigned local variable 'i'" if not using `for` init block For(() => i = 0, () => i < 10, () => i++, () => { if ( i == 5 ) Continue(); Console.WriteLine(i); } ); } |
如果我没有错的话,这种方法与函数式编程实践非常相关。我说的对吗?
我不知道可以帮助避免使用方法重载的泛型方法。下面是用于打印int和double数字的重载方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private static void printNumbers(int [] intNumbers) { foreach(int element in intNumbers) { Console.WriteLine(element); } } private static void printNumbers(double[] doubleNumbers) { foreach (double element in doubleNumbers) { Console.WriteLine(element); } } |
有助于上述两种方法都有一个方法的泛型方法
1 2 3 4 5 6 7 | private static void printNumbers<E>(E [] Numbers) { foreach (E element in Numbers) { Console.WriteLine(element); } } |
阻止DataGridView显示属性:
1 2 | [System.ComponentModel.Browsable(false)] public String LastActionID{get; private set;} |
允许为组件(如DataGrid或DataGridView)设置友好显示:
1 2 | [System.ComponentModel.DisplayName("Last Action")] public String LastAction{get; private set;} |
对于您的支持变量,如果您不想直接访问它们,这将使其变得更困难:
1 2 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] private DataController p_dataSources; |
首先是-debuggertypeproxy。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | [DebuggerTypeProxy(typeof(HashtableDebugView))] class MyHashtable : Hashtable { private const string TestString = "This should not appear in the debug window."; internal class HashtableDebugView { private Hashtable hashtable; public const string TestStringProxy = "This should appear in the debug window."; // The constructor for the type proxy class must have a // constructor that takes the target type as a parameter. public HashtableDebugView(Hashtable hashtable) { this.hashtable = hashtable; } } } |
其次:
ICustomtypeDescriptor(ICustomtypeDescriptor)
"todo"属性和任务列表
1 | //TODO: [something] |
将其添加到代码中(间距很重要)会在任务列表中引发一个项目,双击该项目会将您跳转到代码中的适当位置。
如果试图从项目列表中创建逗号分隔的字符串:
1 2 3 4 | string[] itemList = {"Example 1","Example 2","Example 3" }; CommaDelimitedStringCollection commaStr = new CommaDelimitedStringCollection(); commaStr.AddRange(itemList); //outputs Example 1,Example 2,Example 3 |
请看这里的另一个例子。
PreviousPage属性:
"system.web.ui.page表示将控件传输到当前页的页。"
它非常有用。
不确定微软会喜欢这个问题,尤其是有这么多的回复。我确信我曾经听过一位微软主管说:
a hidden feature is a wasted feature
…或是有这种效果的东西。
@罗比火箭队
"but my instincts tell me that this
would cut a maximum of two type casts
operations down to a maximum of one."
如果您按照示例1中的建议进行转换(使用is&as),则会导致对"is"运算符进行两次调用。因为当您将"c=obj作为myclass"时,首先它在后台调用"is",然后如果失败,它只返回空值。
如果您按照示例2中的建议进行演员阵容,
1 | c = (MyClass)obj |
然后它再次执行"is"操作,如果检查失败,则抛出异常(invalidCastException)。
因此,如果您想进行轻量级动态投射,最好使用您提供的第三个示例:
1 2 3 4 5 6 7 8 9 |
VS
1 2 3 4 5 | MyClass c = obj as MyClass; if (c != null) { } |
你可以看到哪个更快、更贴心、更清晰。
在处理C++和C语言之间的互操作时,很多人没有意识到C++/CLI是一个很好的选择。
假设你有一个C++的DLL和一个依赖于C++ DLL的C语言的DLL。通常,最简单的技术是用CL/CLR开关编译C++(DLL)的一些(或全部)模块。让C++调用C++ DLL是在C++ DLL中编写托管C++封装类。C++/CLI类可以比C++更无缝地调用本机C++代码,因为C++编译器将自动为您生成P/调用,有专门用于互操作的库,加上PopyPTR互操作的语言特性。它允许托管代码和本机代码在同一个二进制文件中共存。
在C端,您只需像调用任何其他.NET二进制文件一样调用dll。
在包含转义字符的字符串之前使用@。基本上,当使用物理路径在字符串变量中赋值时,每个人都使用",其中转义符出现在字符串中。
例如string strpath="d:websitesweb1images";
但转义字符可以在字符串值之前使用@忽略。
例如string strpath=@"d:websitesweb1images";
通过yield获取IEnumerable的另一种方法,而不显式地创建IEnumerable对象
1 2 3 4 5 | public IEnumerable<Request> SomeMethod(IEnumerable<Request> requests) { foreach (Request request in requests) yield return DoSomthing(request); } |
BCL中的一些并发实用程序可能被定义为隐藏功能。
锁锁关键字内部使用Stask.Tyrngn.Max这样的东西;在C语言中,锁关键字是可取的,但有时知道如何在较低的级别上完成是值得的;我必须锁在C++/CLI中,所以我把一个代码块封装到MaultCuff.In()和监视器.Ext()中。
使用delegate.createDelegate调用私有方法的技巧非常简单。
1 2 3 4 |
这是一个有用的上下文
上面提到了list.foreach;2.0引入了一系列基于谓词的集合操作-find、findall、exists等。再加上匿名委托,您几乎可以实现3.5 lambda表达式的简单性。
I think if you have to use nullable
types, it's better to use Nullable<.T>
rather than the question mark
notation. It makes it eye-achingly
obvious that magic is occurring. Not
sure why anyone would ever want to use
Nullable<.bool> though.
在VB.NET Web服务中,参数可能无法传递(因为伙伴请求不一致或不可靠),但必须通过针对建议类型的验证(布尔值表示"if is search request")。把它归结为"管理层的另一个需求"…
…是的,我知道有些人认为这不是正确的方法来做这些事情,但我早就被看作是可以为空的(布尔值),这让我那天晚上失去了理智!
小心使用时,释放功能非常强大。我在电子邮件模板系统中使用它。模板管理器将传递一个对象,HTML模板将具有引用属性的嵌入字段,这些属性可以使用反射从传递的对象中检索出来。做得很好。
在lambda生效之前,它是匿名委托。这可以用于类似Ruby的blockgiven的覆盖代码。不过,我还没有测试lambda的工作原理,因为到目前为止我还想坚持使用.NET 2.0。
例如,当您要确保记住关闭HTML标记时:
1 2 3 4 5 6 7 8 9 10 11 | MyHtmlWriter writer=new MyHtmlWriter(); writer.writeTag("html", delegate () { writer.writeTag("head", delegate() { writer.writeTag("title"...); } ) }) |
我确信如果lambda是一个选项,那将产生更清晰的代码:)
大多数p/invoke的内容都有点奇怪。
属性示例:
1 2 3 | [DllImport ("gdi32.dll")] [return : MarshalAs(UnmanagedType.I4)] [StructLayout(LayoutKind.Sequential)] |
我必须承认,我不确定这是否比正常的ASP.NET转发器OnitemDataboundCast代码更好或更差,但无论如何,这里是我的5美分。
1 2 3 4 5 | MyObject obj = e.Item.DataItem as MyObject; if(obj != null) { //Do work } |
下面是如何使用region指令来记录代码的提示。
一些?怪异:
1 2 3 4 5 6 7 8 | Delegate target = (target0 = target as CallTargetWithContext0) ?? (target1 = target as CallTargetWithContext1) ?? (target2 = target as CallTargetWithContext2) ?? (target3 = target as CallTargetWithContext3) ?? (target4 = target as CallTargetWithContext4) ?? (target5 = target as CallTargetWithContext5) ?? ((Delegate)(targetN = target as CallTargetWithContextN)); |
有趣的是注意到最后一个演员是出于某种原因需要的。错误还是设计?
异常筛选器。所以"隐藏"在没有编译后补丁的情况下,你甚至不能使用它们(至少从C)
根据周围类的泛型类型分离静态字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public class StaticConstrucEx2Outer<T> { // Will hold a different value depending on the specicified generic type public T SomeProperty { get; set; } static StaticConstrucEx2Outer() { Console.WriteLine("StaticConstrucEx2Outer" + typeof(T).Name); } public class StaticConstrucEx2Inner<U, V> { static StaticConstrucEx2Inner() { Console.WriteLine("Outer <{0}> : Inner <{1}><{2}>", typeof(T).Name, typeof(U).Name, typeof(V).Name); } public static void FooBar() {} } public class SCInner { static SCInner() { Console.WriteLine("SCInner init <{0}>", typeof(T).Name); } public static void FooBar() {} } } StaticConstrucEx2Outer<int>.StaticConstrucEx2Inner<string, DateTime>.FooBar(); StaticConstrucEx2Outer<int>.SCInner.FooBar(); StaticConstrucEx2Outer<string>.StaticConstrucEx2Inner<string, DateTime>.FooBar(); StaticConstrucEx2Outer<string>.SCInner.FooBar(); StaticConstrucEx2Outer<string>.StaticConstrucEx2Inner<string, Int16>.FooBar(); StaticConstrucEx2Outer<string>.SCInner.FooBar(); StaticConstrucEx2Outer<string>.StaticConstrucEx2Inner<string, UInt32>.FooBar(); StaticConstrucEx2Outer<long>.StaticConstrucEx2Inner<string, UInt32>.FooBar(); |
将产生以下输出
1 2 3 4 5 6 7 8 9 10 11 | Outer <Int32> : Inner <String><DateTime> SCInner init <Int32> Outer <String> : Inner <String><DateTime> SCInner init <String> Outer <String> : Inner <String><Int16> Outer <String> : Inner <String><UInt32> Outer <Int64> : Inner <String><UInt32> |
当需要(a)在对象之间同步通信有关事件发生的信息时,有一个称为isynchronizeinvoke的特殊用途接口。
引用msdn文章(链接):
Objects that implement this interface can receive notification that an event has occurred, and they can respond to queries about the event. In this way, clients can ensure that one request has been processed before they submit a subsequent request that depends on completion of the first.
下面是一个通用包装器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | protected void OnEvent<T>(EventHandler<T> eventHandler, T args) where T : EventArgs { if (eventHandler == null) return; foreach (EventHandler<T> singleEvent in eventHandler.GetInvocationList()) { if (singleEvent.Target != null && singleEvent.Target is ISynchronizeInvoke) { var target = (ISynchronizeInvoke)singleEvent.Target; if (target.InvokeRequired) { target.BeginInvoke(singleEvent, new object[] { this, args }); continue; } } singleEvent(this, args); } } |
下面是一个示例用法:
1 2 3 4 5 6 | public event EventHandler<ProgressEventArgs> ProgressChanged; private void OnProgressChanged(int processed, int total) { OnEvent(ProgressChanged, new ProgressEventArgs(processed, total)); } |
如果要防止垃圾收集器运行对象的终结器,只需使用
不是隐藏的,而是非常整洁的。我发现这是一个更简洁的替代品,简单的if-then-else只根据条件赋值。
1 2 3 4 5 6 7 8 | string result = i < 2 ? //question "less than 2" : //answer i < 5 ? //question "less than 5": //answer i < 10 ? //question "less than 10": //answer "something else"; //default answer |
如果允许第三方扩展,那么必须知道c5和Microsoft CCR(有关快速介绍,请参阅此博客文章)。
C5补充了.NET有点缺乏的集合库(没有设置???)和CCR使得并发编程更容易(我听说它应该与并行扩展合并)。
我不知道这是否是隐藏的功能(")。任何字符串函数。