【问题标题】:Hidden Features of C#? [closed]C#的隐藏特性? [关闭]
【发布时间】:2010-09-05 18:14:36
【问题描述】:

this question 了解到以下内容后,我想到了这一点:

where T : struct

我们,C# 开发人员,都知道 C# 的基础知识。我的意思是声明、条件、循环、运算符等。

我们中的一些人甚至掌握了诸如Genericsanonymous typeslambdasLINQ、...之类的东西。

但 C# 最隐秘的特性或技巧是什么,即使是 C# 爱好者、瘾君子、专家也几乎不知道?

以下是目前已揭示的功能:


关键词

属性

语法

语言特点

Visual Studio 功能

框架

方法和属性

提示和技巧

  • Andreas H.R. Nilsson 的事件处理程序的好方法
  • John 的大写比较
  • 通过dp 访问匿名类型而不进行反射
  • Will 懒惰实例化集合属性的快速方法
  • roosteronacid 的类似 JavaScript 的匿名内联函数

其他

【问题讨论】:

    标签: c# hidden-features


    【解决方案1】:

    lambdas 和类型推断 被低估了。 Lambda 可以有多个语句,它们会自动兼作兼容的委托对象(只需确保签名匹配),如下所示:

    Console.CancelKeyPress +=
        (sender, e) => {
            Console.WriteLine("CTRL+C detected!\n");
            e.Cancel = true;
        };
    

    请注意,我没有new CancellationEventHandler,也不必指定sendere 的类型,它们可以从事件中推断出来。这就是为什么编写整个delegate (blah blah) 不那么麻烦的原因,这还需要您指定参数类型。

    Lambda 不需要返回任何内容,并且类型推断在这样的上下文中非常强大。

    顺便说一句,在函数式编程的意义上,您总是可以返回 制作 Lambda 的 Lambda。例如,这里有一个 lambda,它生成一个处理 Button.Click 事件的 lambda:

    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);
    

    注意链接:(dx, dy) =&gt; (sender, e) =&gt;

    这就是我很高兴参加函数式编程课程的原因:-)

    除了 C 语言中的指针,我认为这是你应该学习的另一个基本知识 :-)

    【讨论】:

      【解决方案2】:

      几乎所有酷的都被提及了。不确定这个是否众所周知

      C# 属性/字段构造函数初始化:

      var foo = new Rectangle() 
      { 
          Fill = new SolidColorBrush(c), 
          Width = 20, 
          Height = 20 
      };
      

      这将创建矩形,并设置列出的属性。

      我注意到一些有趣的事情 - 您可以在属性列表的末尾添加一个逗号,而不会出现语法错误。所以这也是有效的:

      var foo = new Rectangle() 
      { 
          Fill = new SolidColorBrush(c), 
          Width = 20, 
          Height = 20,
      };
      

      【讨论】:

      • 末尾的逗号使摆弄这些值变得更加容易:)
      • 尾随逗号对于生成的代码也很有用。您会注意到它适用于许多情况。在进行枚举时,我最常遇到它。 :)
      • 枚举还支持尾随逗号“功能”。
      • Rectangle() 中的 () 也不需要
      【解决方案3】:

      Conditional string.Format:

      根据数字是正数、负数还是零,对数字应用不同的格式。

      string s = string.Format("{0:positive;negative;zero}", i);
      

      例如

      string format = "000;-#;(0)";
      
      string pos = 1.ToString(format);     // 001
      string neg = (-1).ToString(format);  // -1
      string zer = 0.ToString(format);     // (0)
      

      【讨论】:

      • 这类似于reg表达式,非常有用,但我也记不住了。我用 padleft 和 padright 处理上面的东西。
      • 酷,我从来不知道这是可能的......它记录在任何地方吗?
      • @Thomas:它记录在 MSDN 中自定义数字格式主题末尾的“;”部分分隔符部分中:msdn.microsoft.com/en-us/library/0c899ak8.aspx
      【解决方案4】:

      您可以组合protectedinternal 访问器,使其在同一个程序集中公开,但在不同程序集中受到保护。这可以用于字段、属性、方法甚至常量。

      【讨论】:

        【解决方案5】:

        我想如果你必须使用 nullable 类型,最好使用 Nullable<.t> 而不是问号 符号。让人眼花缭乱 很明显,魔法正在发生。不是 确定为什么有人会想要使用 可空<.bool>。

        在 VB.NET Web 服务中,参数可能无法通过(因为合作伙伴的请求不一致或不可靠),但必须通过针对建议类型的验证 (“如果是搜索请求”的布尔值)。把它归结为“管理层的另一个要求”......

        ...是的,我知道有些人认为这不是做这些事情的正确方法,但是 IsSearchRequest As Nullable(Of Boolean) 让我那天晚上失去了理智!

        【讨论】:

          【解决方案6】:

          P/Invoke 的大部分内容都有些奇怪。

          属性示例:

          [DllImport ("gdi32.dll")] 
          [return : MarshalAs(UnmanagedType.I4)]
          [StructLayout(LayoutKind.Sequential)]
          

          【讨论】:

            【解决方案7】:

            其他一切,加上

            1) 隐式泛型(为什么只在方法上而不在类上?)

            void GenericMethod<T>( T input ) { ... }
            
            //Infer type, so
            GenericMethod<int>(23); //You don't need the <>.
            GenericMethod(23);      //Is enough.
            

            2) 带有一个参数的简单 lambda:

            x => x.ToString() //simplify so many calls
            

            3) 匿名类型和初始化器:

            //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) 自动属性可以有不同的作用域:

            public int MyId { get; private set; }
            

            感谢@pzycoman 提醒我:

            5) 命名空间别名(并不是说您可能需要这种特殊的区别):

            using web = System.Web.UI.WebControls;
            using win = System.Windows.Forms;
            
            web::Control aWebControl = new web::Control();
            win::Control aFormControl = new win::Control();
            

            【讨论】:

            • 在#3 你可以做 Enumerable.Range(1,5)
            • 我认为您已经能够使用 int[] nums = {1,2,3}; 初始化数组;从 1.0 开始 :) 甚至不需要“new”关键字
            • 也是不带参数的lambda ()=> DoSomething();
            • 我都用过 { get;内部集; } 和 { 得到;受保护的集合; },所以这个模式是一致的。
            • @Kirk Broadhurst - 你是对的 - new web.Control() 在这个例子中也可以工作。 :: 语法强制它将前缀视为命名空间别名,因此您可以有一个名为 web 的类,web::Control 语法仍然有效,而 web.Control 语法不知道是否检查类或命名空间。正因为如此,我在做命名空间别名时倾向于总是使用::
            【解决方案8】:

            您可以使用{ } 括号来限制变量的生命周期和范围。

            {
                string test2 = "3";
                Console.Write(test2);
            }
            
            Console.Write(test2); //compile error
            

            test2 只存在于括号内。

            【讨论】:

            • C++ 也是如此。
            • 对没有词法作用域的任何语言都成立?
            • hmmm...这是隐藏功能吗??
            【解决方案9】:

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

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

            变成

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

            【讨论】:

            • 这也可以很容易地更改为空安全:var isEqual = String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
            • 但是...这不是 C# 功能,它是 .Net 框架的功能,更具体地说是“String”类的功能
            【解决方案10】:

            您输入“prop”,然后按 [TAB] 两次,它会为您的属性生成有用的代码并可以加快您的输入速度。

            我知道这在 VS 2005 中有效(我使用它),但我不知道在以前的版本中。

            【讨论】:

            • 我认为是 VS 2005 及更高版本。我经常使用它:-)
            • Coderush 将其缩短为 'p' ;)
            • 这本身不是一个功能。这是一个 Visual Studio 代码 sn-p。您可以创建自己的 sn-ps,甚至可以在 Internet 上找到一些。例如,查看 Dr WPF 的代码 sn-ps。
            • ctor 可用于创建构造函数
            • Ctrl K, Ctrl X 显示了 Visual Studio 中所有可用的 sn-ps。
            【解决方案11】:

            C# + CLR:

            1. Thread.MemoryBarrier: 大多数人不会用它,而且 MSDN 上有一些不准确的信息。但是,如果您知道intricacies,那么您可以进行漂亮的无锁同步。

            2. volatile, Thread.VolatileRead, Thread.VolatileWrite: 很少有人会使用这些,了解他们避免和引入的所有风险的人就更少了:)。

            3. ThreadStatic变量:前几年只有一种情况我发现ThreadStatic变量绝对是天赐良机,不可或缺。例如,当您想为整个调用链做某事时,它们非常有用。

            4. fixed 关键字:当您希望访问大型数组的元素几乎与 C++ 一样快时,这是一个隐藏的武器(默认情况下,C# 强制执行绑定检查,这会减慢速度)。

              李>
            5. default(typeName) 关键字也可以在泛型类之外使用。创建结构的空副本很有用。

            6. 我使用的一个方便的功能是DataRow[columnName].ToString() 总是返回非空值。如果数据库中的值为 NULL,则得到空字符串。

            7. 即使她/他没有启用异常自动中断,您也可以在需要开发人员注意时使用 Debugger 对象自动中断:

            
            #if DEBUG  
                if (Debugger.IsAttached)  
                    Debugger.Break();  
            #endif
            
            1. 您可以为复杂难看的泛型类型设置别名,这样您就不必一次又一次地复制粘贴它们。您也可以在一处更改该类型。例如,
            
                using ComplicatedDictionary = Dictionary<int, Dictionary<string, object>>;
                ComplicatedDictionary myDictionary = new ComplicatedDictionary();
            

            【讨论】:

            • 很好,很棒的提示,在最后一个中,您遇到了标签问题...将
            • 现在大部分都归结为“人们不知道线程背后的机制”恕我直言。这可能是真的,但不接近“隐藏的语言特征”?为 Debugger.Break() 点赞
            【解决方案12】:

            以下是一些有趣的隐藏 C# 功能,以未记录的 C# 关键字的形式:

            __makeref
            
            __reftype
            
            __refvalue
            
            __arglist
            

            这些是未记录的 C# 关键字(甚至 Visual Studio 也能识别它们!),它们被添加到泛型之前用于更有效的装箱/拆箱。它们与 System.TypedReference 结构协同工作。

            还有__arglist,用于变长参数列表。

            人们不太了解的一件事是System.WeakReference——一个非常有用的类,它跟踪对象但仍允许垃圾收集器收集它。

            最有用的“隐藏”功能是 yield return 关键字。它并不是真正隐藏的,但很多人并不知道它。 LINQ 建立在此之上;它允许通过在后台生成状态机来延迟执行查询。 Raymond Chen 最近发布了关于internal, gritty details

            【讨论】:

            【解决方案13】:

            关于foreach:它不使用“鸭子打字”,因为鸭子打字IMO指的是运行时检查。它在编译时使用结构类型检查(与标称相对)来检查类型中所需的方法。

            【讨论】:

            • 是的,鸭子打字也应该保留给全动态恕我直言;但是:foreach does 使用鸭子类型,因为它将在运行时为您强制 .NET 1.1(非通用)GetEnumerator() 迭代器(这恰好是一个陷阱,也是 Cast 的原因太方便了)。
            【解决方案14】:

            我喜欢在如下列表中查找内容:-

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

            而不是:-

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

            在实践中并没有那么多,但是将项目与搜索主题进行匹配的想法确实非常有用 + 简洁。

            【讨论】:

            • 我很好奇编译器为这两种实现吐出了什么。第一个绝对看起来更干净,是一种可爱的方式。如果对象实际上是先在内存中创建然后迭代,可能会更慢。
            • 但是你可以使用 switch 两全其美(至少对于这个例子,或任何整数类型)。示例如下,但由于缺少换行符,cmets 的可读性受到影响:switch(fruit){ case "apple": case "orange": case "banana": case "pear": return true; default: return false; }
            • @P Daddy - 是的,但确实有很多额外的语法。 @Fowl - 如果案例没有代码,您可以失败(当然,直到失败案例)。
            • public static bool In(T value, params T[] items){ return items.Contains(value); } if (fruit.In("apple", "orange", "banana", "pear")) { ... }
            • @Belorus 我认为您将其视为可读性是一门科学,但事实并非如此。是的,它会影响性能,但通常它不会影响程序的整体性能。如果它完成了这项工作,即使它不必要地分配内存,它又是如何“错误”的?可读性是一件非常主观的事情 - 对一个人来说似乎很简单的事情对另一个人来说可能会令人震惊。
            【解决方案15】:

            我参加这个聚会迟到了,所以我的第一选择已经做出。但我还没有看到有人提到这个宝石:

            Parallel Extensions to the .NET Framework

            它有一些东西,比如用 Parallel.For 替换或用 Parallel.ForEach 替换 foreach


            平行样本:
            在您看来,一秒钟可以创建多少个 CLR 对象?
            请看下面的例子:

            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\n", (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\n", (reps / sp.ElapsedMilliseconds) * 1000);
                   }
               }
            }
            

            【讨论】:

            • 这并不是一个真正的语言特性,因为它只是一个巧妙地使用其他语言特性的库。
            【解决方案16】:

            我最喜欢的是

            global::
            

            使用我们的一些 3rd 方代码提供程序来逃离命名空间地狱的关键字...

            例子:

            global::System.Collections.Generic.List<global::System.String> myList =
                new global::System.Collections.Generic.List<global::System.String>();
            

            【讨论】:

            • 它的工作方式类似于访问说明符,但与命名空间有关,即global 与命名空间别名限定符一起使用时,:: 指的是全局命名空间,这是任何 C# 程序的默认命名空间。此处的示例用法 - msdn.microsoft.com/en-us/library/c3ay4x3d.aspx
            【解决方案17】:
            1. 我还不能发表评论,但请注意,默认情况下 Visual Studio 2008 会自动跳过属性,因此在这种情况下不再需要 DebuggerStepThrough 属性。

            2. 另外,我没有注意到有人展示了如何声明一个无参数的 lambda(对于实现 Action 很有用)

              () =&gt; DoSomething(x);

            您还应该阅读闭包 - 我不够聪明,无法正确解释它们。但基本上这意味着编译器做了一些聪明的事情,所以即使在创建 lambda 之后它“超出范围”,那行代码中的 x 仍然可以工作。

            1. 我最近还发现你可以假装忽略一个 lambda 参数:

              (e, _) =&gt; DoSomething(e)

            这并不是真的忽略它,只是 _ 是一个有效的标识符。所以你不能忽略这样的两个参数,但我认为这是一种巧妙的方式来表明我们不关心那个参数(通常是 .Empty 的 EventArgs)。

            【讨论】:

              【解决方案18】:

              我经常遇到需要将通用参数对象持久化到基类的视图状态中。

              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;
                      }
                  }
              
              }
              

              用法:

              private void SomeEventHappened(object sender, EventArgs e)
              {
                  Filters.SomeValue = SomeControl.SelectedValue;
              }
              
              private void TimeToFetchSomeData()
              {
                  GridView.DataSource = Repository.GetList(Filters);
              }
              

              “where ParameterType : BaseParameterType, new”这个小技巧使它真正起作用。

              在我的基类中使用这个属性,我可以自动处理分页、设置过滤器值以过滤网格视图、使排序变得非常容易等等。

              我真的只是说泛型在坏人手中可能是一个非常强大的野兽。

              【讨论】:

              • 也许我很傻,但我不确定我是否理解代码。你能发布一个更完整的例子吗?
              • 其实很简单。如果您有一个带有几个 DropDownLists 的 GridView 来过滤掉内容,您只需将值放入在回发之间持久化的过滤器对象中,并将其作为参数发送到从数据库中获取数据的方法。您只需实现从 BaseListControl 继承的 UserControl,然后 base 负责在回发之间保持“状态”。
              • “小把戏”是什么意思?您是指不能在泛型中创建参数化类型,除非泛型约束包含“新”子句吗?
              • 不,小技巧是限制到 BaseParameterType。有点错别字:\
              【解决方案19】:

              老实说,根据定义,专家应该知道这些东西。但要回答您的问题:Built-In Types Table (C# Reference)

              数字的编译器标记因这些而广为人知:

              Decimal = M
              Float = F
              Double = D
              
              // for example
              double d = 30D;
              

              但是这些比较模糊:

              Long = L
              Unsigned Long = UL
              Unsigned Int = U
              

              【讨论】:

              • 每次处理小数时,我都必须查找 m。是只有我还是 m 不是很直观? :)
              • M 语法来自称为 Money 的旧 VB 类型。 M == 金钱 == 十进制。
              • 不,小于 Int32 的任何内容都会由编译器根据其左侧的类型自动推断
              • @Nick:很好——我喜欢学习代码背后的历史。
              • 我记得 M=Decimal/D=Double 就像我记得左舷/右舷一样:右舷的意思是“右”并且有更多的“R”。 Decimal 有 M 但 Double 没有。
              【解决方案20】:

              我个人最喜欢的两个,我认为很少使用:

              1. 片段(尤其是属性,在 Visual Studio 2008 中做得更好)
              2. ObsoleteAttribute

              【讨论】:

              • 我非常喜欢开关sn-p。使打开枚举变得更加容易;)
              • 我经常使用 sn-p 属性调用设置器中的 OnPropertyChanged。非常方便。
              • sn-ps 是 Visual Studio 的一项功能吗?而不是 C#/编译器?
              • sn-ps 是 Visual Studio 的一部分,快捷键是 ctrl - k+ctrl x
              • 更好,例如:写“for”或“switch”,然后双击“tab”键
              【解决方案21】:

              yield”会出现在我的脑海中。 [DefaultValue()] 等一些属性也是我的最爱。

              var”关键字比较知名,但您也可以在 .NET 2.0 应用程序中使用它(只要您use the .NET 3.5 compiler 并将其设置为输出 2.0 代码)似乎不是众所周知。

              编辑:kokos,感谢您指出 ??运营商,确实很有用。由于搜索它有点困难(因为 ?? 只是被忽略了),这里是该运算符的 MSDN 文档页面:?? Operator (C# Reference)

              【讨论】:

              • 默认值的文档说它并没有真正设置属性的值。它只是可视化器和代码生成器的帮手。
              • 至于 DefaultValue:同时,一些库使用它。 ASP.net MVC 在控制器操作的参数上使用 DefaultValue(这对于不可为空的类型非常有用)。当然,严格来说,这是一个代码生成器,因为值不是由编译器设置的,而是由 MVC 的代码设置的。
              • ?? 的名称运算符是“空合并”运算符
              • yield 是我最喜欢的,但合并运算符在上面。我没有看到任何关于 CAS 或程序集签名、强名称、GAC 的信息……我想只有 CAS 是 C#……但是很多开发人员对安全性一无所知。
              【解决方案22】:

              混音。基本上,如果您想为多个类添加一个特性,但不能为所有类使用一个基类,则让每个类实现一个接口(没有成员)。然后,为接口写一个扩展方法,即

              public static DeepCopy(this IPrototype p) { ... }
              

              当然,牺牲了一些清晰度。但它有效!

              【讨论】:

              • 是的,我认为这是扩展方法的真正威力。它们基本上允许实现接口方法。
              • 如果您使用 NHibernate(或 Castle ActiveRecord)并且您必须为您的集合使用接口,这也很方便。通过这种方式,您可以为集合接口赋予行为。
              • 这基本上就是所有 LINQ 方法的实现方式,记录一下。
              • 这是一个链接,讨论我上面提到的内容,在 NHibernate 或 Castle ActiveRecord 中使用带有集合接口的扩展方法:devlicio.us/blogs/billy_mccafferty/archive/2008/09/03/…
              • 要是他们允许扩展属性就好了!!!!我讨厌不得不编写一个请求成为只读属性的扩展方法....
              【解决方案23】:

              类似 JavaScript 的匿名内联函数

              返回一个字符串:

              var s = new Func<String>(() =>
              {
                  return "Hello World!";
              })();
              

              返回一个更复杂的对象:

              var d = new Func<Dictionary<Int32, String>>(() =>
              {
                  return new Dictionary<Int32, String>
                  {
                      { 0, "Foo" },
                      { 1, "Bar" },
                      { 2, "..." }
                  };
              })();
              

              一个真实的用例:

              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 = "" }
                  }
              );
              

              注意:不能在内联函数的范围内重复使用变量名。


              替代语法

              // 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 "";
              };
              

              缩短字符串并添加水平省略号...

              Func<String, String> Shorten = s => s.Length > 100 ? s.Substring(0, 100) + "&hellip;" : s;
              

              【讨论】:

                【解决方案24】:

                我会说使用某些系统类作为扩展方法非常方便,例如 System.Enum,您可以执行以下操作...

                [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)) {
                    ...
                }
                

                这当然只是一个例子 - 这些方法可能需要更多的工作......

                【讨论】:

                • 不错;但我会使用 Include 而不是 Append 因为 Append 意味着值可能没有的顺序。
                • @devstuff - 这是一个很好的观点,我从来没有从这个角度考虑过。
                • @HBoss:我要使用它。这将使我们更容易以我们应该的方式实现标志。
                • 刚刚发现了一些很棒的东西:所有这些“对象”演员都引起了很多拳击。如果您添加where T : struct 的通用约束(这样 T 不能是引用类型),您实际上可以强制转换 (int)(ValueType)value 并避免装箱:我已经编辑了答案以显示这一点。
                【解决方案25】:

                从方法返回匿名类型并在不反射的情况下访问成员。

                // 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;
                }
                

                【讨论】:

                • 这真的没有给你任何东西。这实际上很危险。如果 GetUserTuple 被修改为返回多种类型怎么办?强制转换将在运行时失败。 C#/.Net 的一大优点是编译时检查。最好只创建一个新类型。
                • @Jason 我确实说过它可能没用,但它令人惊讶(我认为隐藏)。
                • 虽然很酷,但这似乎是一个相当糟糕的设计选择。您基本上在两个地方定义了匿名类型。此时,只需声明一个真正的结构并直接使用它。
                • @George:这样的约定将被称为...结构?
                • 这个技巧被命名为'cast by sample',如果返回匿名类型的方法位于另一个程序集中,它将不起作用。
                【解决方案26】:

                我喜欢滥用静态模板类不共享其静态成员这一事实。

                Type 实例在编译时已知时,这是任何Dictionary&lt;Type,...&gt; 的线程安全(在创建时)和廉价替代品。

                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
                    }
                }
                

                干杯, 弗洛里安

                【讨论】:

                • “任何字典”的替代品?如何?需要解释一下吗?
                • 嗨,看起来我的编辑被删掉了...应该改为“在编译时知道类型时,替换以类型为键的任何字典” - 修复了原始帖子(使用反引号转义括号)
                【解决方案27】:

                另一种通过 yield 获取 IEnumerable 而不显式创建 IEnumerable 对象的方法

                public IEnumerable<Request> SomeMethod(IEnumerable<Request> requests)
                {
                    foreach (Request request in requests)
                       yield return DoSomthing(request);
                }
                

                【讨论】:

                • 当然样本很弱,几乎没有隐藏。我宁愿把它写成requests.Select(DoSomthing) 10 次中的 10 次。 (另外,编辑示例以修复代码错误)
                【解决方案28】:

                在验证用户输入时,每种原始类型的 TryParse 方法都很棒。

                double doubleValue
                if (!Double.TryParse(myDataRow("myColumn"), out doubleValue))
                {
                    // set validation error
                }
                

                【讨论】:

                • 我知道我不是唯一一个忘记“out”修饰符的人。
                • 不返回一个布尔值吗?并设置一个“out”关键字?
                【解决方案29】:

                我在这次谈话中有点晚了,我想贡献以下内容。对于一些开发者来说,这可能是一个新事物。

                public class User
                {
                    public long UserId { get; set; }
                    public String Name { get; set; }
                    public String Password { get; set; }
                    public String Email { get; set; }
                }
                

                声明和初始化它的常用方法是使用构造函数或类似的方法。

                User user = new User();
                user.UserId = 1;
                user.Name = "myname";
                etc
                

                但我学会了以下方法来初始化它。我知道 Visual Basic 开发人员会喜欢它,因为它就像操作符仅在 VB.NET 中可用,而在 C# 中不可用,如下所示。

                User user = new User()
                {
                    UserId = 1,
                    Name = "myname",
                    Email = "myemail@domain.com",
                    Password = "mypassword"
                };
                

                【讨论】:

                • 看看你下面的答案——这已经发布了(我想不止一次)。
                • :) 是的,但它高于我的答案,但它不是总结,所以我贡献了。
                • csharp 3.5 也包含此运算符,您可以在示例中使用自动属性
                • this operator with operator ... With 更棒:)
                【解决方案30】:

                我喜欢的几件事:

                -如果你创建一个类似于

                的界面
                 public interface SomeObject<T> where T : SomeObject<T>, new()
                

                你强制从这个接口继承的任何东西 包含一个无参数的构造函数。这对于一个非常有用的 我遇到了几件事。

                -使用匿名类型动态创建有用的对象:

                var myAwesomeObject = new {Name="Foo", Size=10};
                

                -最后,许多 Java 开发人员都熟悉如下语法:

                public synchronized void MySynchronizedMethod(){}
                

                但是,在 C# 中,这不是有效的语法。解决方法是一个方法属性:

                 [MethodImpl(MethodImplOptions.Synchronized)]
                 public void MySynchronizedMethod(){}
                

                【讨论】:

                • 这些都是好主意。该网站通常更喜欢每个答案一个想法,因此可以单独对它们进行评分。我会给你三个评分:)
                • [MethodImpl(MethodImplOptions.Synchronized)] = lock(this) = bad
                • 我不熟悉“同步”方法,你能告诉我它们是做什么的吗?
                • "你强制从这个接口继承的任何东西都包含一个无参数的构造函数" 严格来说,不,你没有 - 你强制任何实现你的接口的类来证明它知道一个类的名称实现接口并具有无参数构造函数。那不是一回事。 class A : SomeObject { public A() // 必需 } class B : SomeObject { } // 可以正常编译,没有构造函数。
                • @Matt,在这种情况下,同步(或 methodimpl 属性)在方法处理时锁定当前对象。但是,该属性在执行此操作时会导致锁定(此):并且在 CLR 中通过 C# 我记得这不是一个好主意(iirc,它暴露了一个潜在的安全漏洞,但本书一直在另一边房子,真的很晚了)。这就是为什么大多数人会锁定私有对象成员变量而不是 lock(this)。
                猜你喜欢
                • 2010-10-12
                • 2010-10-20
                • 2010-11-05
                • 2010-10-17
                • 1970-01-01
                • 1970-01-01
                • 2010-09-17
                • 2011-03-19
                • 1970-01-01
                相关资源
                最近更新 更多