【问题标题】:With block equivalent in C#?在 C# 中使用块等价物?
【发布时间】:2010-10-03 15:53:25
【问题描述】:

我知道 VB.Net 并且正在努力复习我的 C#。 C# 中是否有 With 块等价物?

【问题讨论】:

标签: c# .net vb.net


【解决方案1】:

尽管 C# 对于一般情况没有任何直接等效项,但 C# 3 为构造函数调用提供了对象初始化语法:

var foo = new Foo { Property1 = value1, Property2 = value2, etc };

更多详细信息请参阅 C# 的第 8 章 - 您可以从 Manning's web site 免费下载。

(免责声明 - 是的,让更多人掌握这本书符合我的利益。但是,嘿,这是一个免费章节,可以为您提供有关相关主题的更多信息......)

【讨论】:

  • 这根本不一样,因为这涉及到创建一个类的实例,而 With 块没有。
  • @JohnStock:因此“一般情况下没有直接等价物”。但是,在您使用 with With 语句仅用于初始化的情况下,它仍然是相应的习惯用法。
  • 这个初始化签名主要是 Lambda 表达式实现的产物,而不是其他任何东西。它是 C# 中最接近 VB 的 With short-hand 的签名。
  • @GoldBishop:它独立于 lambda 表达式 - 但两者都是为了支持 C# 3 中的 LINQ 而创建的。
  • @maf-soft:好吧,github.com/dotnet/csharplang 会是这个地方,但我看不到它的发生。我可以想象with 被用于不可变类型,但不是一般的 VB 案例。
【解决方案2】:

这是 Visual C# 程序经理必须说的: Why doesn't C# have a 'with' statement?

很多人,包括 C# 语言设计者,都相信 'with' 通常会损害可读性,与其说是祝福,不如说是诅咒。它是 更清楚地声明具有有意义名称的局部变量,并使用 该变量对单个对象执行多个操作,而不是 它是有一个带有某种隐式上下文的块。

【讨论】:

  • 死链接。我认为这是它的新点:blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx
  • 链接又死了。这是 C# 常见问题解答中的一个新问题:blogs.msdn.com/b/csharpfaq/archive/2004/03/11/…
  • 完全不同意。我无法格式化我想在评论中使用的代码示例,但现在我有一个完整的get 散布着SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization 而不是With SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization { .AuthToken = "123" .RefreshToken = "456" ... }
  • 除非您正在使用一个相当不寻常的库,否则您没有理由不能缓存授权对象。 var auth = SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization; auth.AuthToken = "123"; auth.RefreshToken = "456"; ... 。换句话说,您的问题可以通过完全按照您所抱怨的答案的规定来解决。
  • 你应该总是给你的变量起有意义的名字。尽管如此,如果您可以为新创建的对象创建单独的范围,它有时会很有用。到目前为止,通过 { var o = new object(); o.use (); ..} 手动创建该范围是实现这一目标的唯一好方法。
【解决方案3】:

正如上面链接的 Visual C# 程序管理器所说,在有限的情况下,With 语句更有效,他给出的示例是当它被用作重复访问复杂表达式的速记时。

使用扩展方法和泛型,您可以创建与 With 语句大致等效的内容,方法是添加如下内容:

    public static T With<T>(this T item, Action<T> action)
    {
        action(item);
        return item;
    }

举一个简单的例子来说明如何使用它,使用 lambda 语法,您可以使用它来更改如下内容:

    updateRoleFamily.RoleFamilyDescription = roleFamilyDescription;
    updateRoleFamily.RoleFamilyCode = roleFamilyCode;

到这里:

    updateRoleFamily.With(rf =>
          {
              rf.RoleFamilyDescription = roleFamilyDescription;
              rf.RoleFamilyCode = roleFamilyCode;
          });

在这样的示例中,唯一的优势可能是更好的布局,但使用更复杂的引用和更多属性,它可以为您提供更易读的代码。

【讨论】:

  • 我并没有真正看到您的示例所显示的优势。原始代码(前 lambda)是 [objectinstance].[property] = [value]。 lambda 代码基本上只是用 rf 更改 updateRoleFamily。
  • 尝试使用对对象实例的更长引用和更多属性。在上面的示例中,您将 updateRoleFamily 简化为 rf 并设置两个属性(您是正确的)并不是一个很大的收获。但是,如果您的对象实例类似于 myDataStructure.GetButton(44),并且您必须设置十个属性,则使用 lambda 或设置局部变量可以使其更具可读性。就像最初的 VB With 语句一样,它只是一点点语法糖,你可以接受或离开。
  • @DanAppleyard 我将其简单地视为封装,它有时非常有用。但后来我有偏见,来自 VB.NET,With 经常派上用场,我真的很想念 C# 中的它。任何熟悉 C# 的人都没有可读性问题,只是让编码更容易一些,尤其是。处理长对象名称时。
  • 似乎设置一个局部变量会更有效且更具可读性。 var rf = myDataStructure.GetButton(44); 我根本不认为这是语法糖。这只是设置局部变量的一种复杂方式。 “仅仅因为你可以并不意味着你应该。”
  • 解决这个非问题的方法是选择更有意义且不那么冗长的名称开头:去掉无用的 RoleFamily 前缀,您将不需要任何这些复杂的解决方法。
【解决方案4】:

在“Using Objects”部分的页面下方大约 3/4:

VB:

With hero 
  .Name = "SpamMan" 
  .PowerLevel = 3 
End With 

C#:

//No "With" construct
hero.Name = "SpamMan"; 
hero.PowerLevel = 3; 

【讨论】:

    【解决方案5】:

    不,没有。

    【讨论】:

      【解决方案6】:

      with 键是在 C# 版本 9 中引入的! 您可以使用它来创建对象的副本,如下所示

      Person brother = person with { FirstName = "Paul" };
      

      "上面的行创建了一个新的 Person 记录,其中 LastName 属性是 person 的副本,FirstName 是“Paul”。您可以在 with 表达式中设置任意数量的属性。除了“克隆”方法可能由您编写。如果记录类型具有与任何合成方法的签名匹配的方法,则编译器不会合成该方法。”

      更新:

      在编写此答案时,C#9 尚未正式发布,仅处于预览阶段。但是,planned 将在 2020 年 11 月与 .NET 5.0 一起发布

      如需更多信息,请查看record types

      【讨论】:

        【解决方案7】:

        最简单的语法是:

        {
            var where = new MyObject();
            where.property = "xxx";
            where.SomeFunction("yyy");
        }
        
        {
            var where = new MyObject();
            where.property = "zzz";
            where.SomeFunction("uuu");
        }
        

        实际上,如果您想重用变量名,像这样的额外代码块非常方便。

        【讨论】:

        • 虽然我希望他们最终添加一个 With 块,但我喜欢这种方法,因为它不需要任何额外的库或自定义代码,并且它成功地防止了对象在范围之外发生变异.
        【解决方案8】:

        我所做的是使用 csharp ref 关键字。例如:

        ref MySubClassType e = ref MyMainClass.MySubClass;
        

        然后您可以使用以下快捷方式: e.property 而不是 MyMainClass.MySubClass.property

        【讨论】:

        • 这称为ref locals,是在 C# 7.0 中引入的。
        【解决方案9】:

        您可以使用参数累加器模式。

        关于这个的大讨论在这里:

        http://blogs.msdn.com/csharpfaq/archive/2004/03/11/87817.aspx

        【讨论】:

        • 这是一个很好的替代方案,除了替换“with”之外,该模式很好用。另一种选择是实用方法,就像我在这里描述的那样:stackoverflow.com/questions/601153/…
        • 链接无效。请在将来包含一个代码 sn-p。谢谢。
        【解决方案10】:

        有时您可以通过以下方式摆脱困境:

        var fill = cell.Style.Fill;
        fill.PatternType = ExcelFillStyle.Solid;
        fill.BackgroundColor.SetColor(Color.Gray);
        fill.PatternColor = Color.Black;
        fill.Gradient = ...
        

        (EPPLus 的代码示例@http://zeeshanumardotnet.blogspot.com

        【讨论】:

          【解决方案11】:

          我是这样用的:

                  worksheet.get_Range(11, 1, 11, 41)
                      .SetHeadFontStyle()
                      .SetHeadFillStyle(45)
                      .SetBorders(
                          XlBorderWeight.xlMedium
                          , XlBorderWeight.xlThick
                          , XlBorderWeight.xlMedium
                          , XlBorderWeight.xlThick)
                      ;
          

          SetHeadFontStyle / SetHeadFillStyle 是 Range 的 ExtMethod,如下所示:

           public static Range SetHeadFillStyle(this Range rng, int colorIndex)
           {
               //do some operation
               return rng;
           }
          

          做一些操作并返回范围以供下次操作

          它看起来像 Linq :)

          但现在仍然不能完全像它——属性设置值

          with cell.Border(xlEdgeTop)
             .LineStyle = xlContinuous
             .Weight = xlMedium
             .ColorIndex = xlAutomatic
          

          【讨论】:

            【解决方案12】:

            这里是With 的忠实粉丝!

            这实际上是我当前的 C# 代码:

            if (SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization.AccessTokenExpiry == null || SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization.AccessTokenExpiry < DateTime.Now)
            {
                SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization.Refresh();
                _api = new SKYLib.AccountsPayable.Api.DefaultApi(new SKYLib.AccountsPayable.Client.Configuration { DefaultHeader = SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization.ApiHeader });
            }
            

            在 VB 中可能是:

            With SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization
                If .AccessTokenExpiry Is Nothing OrElse .AccessTokenExpiry < Now Then .Refresh()
                _api = New SKYLib.AccountsPayable.Api.DefaultApi(New SKYLib.AccountsPayable.Client.Configuration With {DefaultHeader = .ApiHeaders}
            End With
            

            我想清楚多了。您甚至可以通过调整 With 变量将其调整为更简洁。而且,就风格而言,我还有一个选择!可能是 C# 项目经理忽略了一些东西。

            顺便说一句,这种情况并不常见,但我偶尔会使用它:

            代替

            Using oClient As HttpClient = New HttpClient
                With oClient
                    .BaseAddress = New Uri("http://mysite")
                    .Timeout = New TimeSpan(123)
                    .PostAsync( ... )
                End With
            End Using
            

            你可以使用

            With New HttpClient
                .BaseAddress = New Uri("http://mysite")
                .Timeout = New TimeSpan(123)
                .PostAsync( ... )
            End With
            

            你冒着被打耳光的风险——我发帖也是如此! - 但您似乎在处置等方面获得了 Using 声明的所有好处,而无需额外的繁琐。

            注意:这偶尔会出错,因此请仅将其用于非关键代码。或者根本没有。请记住:您可以选择...

            【讨论】:

              【解决方案13】:

              为了让生活更轻松,您可以使用垂直选择来快速编辑内容

              http://joelabrahamsson.com/select-columns-of-text-in-visual-studio/

              【讨论】:

                【解决方案14】:

                还有一个有趣的 with-pattern 实现

                public static T With<T>(this T o, params object[] pattern) => o;
                public static T To<T>(this T o, out T x) => x = o;
                

                您可以通过the link 浏览更多详细信息并研究online code samples

                用法的变化

                static Point Sample0() => new Point().To(out var p).With(
                    p.X = 123,
                    p.Y = 321,
                    p.Name = "abc"
                );
                
                public static Point GetPoint() => new Point { Name = "Point Name" };
                static string NameProperty { get; set; }
                static string NameField;
                
                static void Sample1()
                {
                    string nameLocal;
                    GetPoint().To(out var p).With(
                        p.X = 123,
                        p.Y = 321,
                        p.Name.To(out var name), /* right side assignment to the new variable */
                        p.Name.To(out nameLocal), /* right side assignment to the declared var */
                        NameField = p.Name, /* left side assignment to the declared variable */
                        NameProperty = p.Name /* left side assignment to the property */
                    );
                
                    Console.WriteLine(name);
                    Console.WriteLine(nameLocal);
                    Console.WriteLine(NameField);
                    Console.WriteLine(NameProperty);
                }
                
                static void Sample2() /* non-null propogation sample */
                {
                    ((Point)null).To(out var p)?.With(
                        p.X = 123,
                        p.Y = 321,
                        p.Name.To(out var name)
                    );
                
                    Console.WriteLine("No exception");
                }
                
                static void Sample3() /* recursion */
                {
                    GetPerson().To(out var p).With(
                        p.Name.To(out var name),
                        p.Subperson.To(out var p0).With(
                            p0.Name.To(out var subpersonName0)
                        ),
                        p.GetSubperson().To(out var p1).With( /* method return */
                            p1.Name.To(out var subpersonName1)
                        )
                    );
                
                    Console.WriteLine(subpersonName0);
                    Console.WriteLine(subpersonName1);
                }
                

                如果您使用结构 [值类型],类似的扩展方法也会很有用

                public static TR Let<T, TR>(this T o, TR y) => y;
                

                可以在With方法之后应用,因为默认会返回struct的未修改副本

                struct Point
                {
                    public double X;
                    public double Y;
                    public string Name;
                }
                
                static Point Sample0() => new Point().To(out var p).With(
                    p.X = 123,
                    p.Y = 321,
                    p.Name = "abc"
                ).Let(p);
                

                喜欢就尽情享受吧!

                【讨论】:

                  【解决方案15】:

                  我认为要“使用”的壁橱是static using,但仅适用于静态的方法或属性。 例如

                  using static System.Math;
                  ...
                  public double Area
                  {
                     get { return PI * Pow(Radius, 2); } // PI == System.Math.PI
                  }
                  

                  更多信息: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-static

                  【讨论】:

                    【解决方案16】:

                    对我来说,我试图自动生成代码,并且需要为多个不同的类重用像“x”这样的简单变量,这样我就不必不断生成新的变量名。我发现如果我只是将代码放在花括号部分 {} 中,我可以限制变量的范围并多次重复使用它。

                    看例子:

                    public class Main
                    {
                        public void Execute()
                        {
                            // Execute new Foos and new Bars many times with same variable.
                            double a = 0;
                            double b = 0;
                            double c = 0;
                            double d = 0;
                            double e = 0;
                            double f = 0;
                    
                            double length = 0;
                            double area = 0;
                            double size = 0;
                    
                            {
                                Foo x = new Foo(5, 6).Execute();
                                a = x.A;
                                b = x.B;
                                c = x.C;
                                d = x.D;
                                e = x.E;
                                f = x.F;
                            }
                            {
                                Bar x = new Bar("red", "circle").Execute();
                                length = x.Length;
                                area = x.Area;
                                size = x.Size;
                            }
                            {
                                Foo x = new Foo(3, 10).Execute();
                                a = x.A;
                                b = x.B;
                                c = x.C;
                                d = x.D;
                                e = x.E;
                                f = x.F;
                            }
                            {
                                Bar x = new Bar("blue", "square").Execute();
                                length = x.Length;
                                area = x.Area;
                                size = x.Size;
                            }
                        }
                    }
                    
                    public class Foo
                    {
                        public int X { get; set; }
                        public int Y { get; set; }
                        public double A { get; private set; }
                        public double B { get; private set; }
                        public double C { get; private set; }
                        public double D { get; private set; }
                        public double E { get; private set; }
                        public double F { get; private set; }
                    
                        public Foo(int x, int y)
                        {
                            X = x;
                            Y = y;
                        }
                    
                        public Foo Execute()
                        {
                            A = X * Y;
                            B = X + Y;
                            C = X / (X + Y + 1);
                            D = Y / (X + Y + 1);
                            E = (X + Y) / (X + Y + 1);
                            F = (Y - X) / (X + Y + 1);
                            return this;
                        }
                    }
                    
                    public class Bar
                    {
                        public string Color { get; set; }
                        public string Shape { get; set; }
                        public double Size { get; private set; }
                        public double Area { get; private set; }
                        public double Length { get; private set; }
                        
                        public Bar(string color, string shape)
                        {
                            Color = color;
                            Shape = shape;
                        }
                    
                        public Bar Execute()
                        {
                            Length = Color.Length + Shape.Length;
                            Area = Color.Length * Shape.Length;
                            Size = Area * Length;
                            return this;
                        }
                    }
                    

                    在 VB 中,我会使用 With 而根本不需要变量“x”。排除Foo和Bar的vb类定义,vb代码为:

                    Public Class Main
                        Public Sub Execute()
                            Dim a As Double = 0
                            Dim b As Double = 0
                            Dim c As Double = 0
                            Dim d As Double = 0
                            Dim e As Double = 0
                            Dim f As Double = 0
                            Dim length As Double = 0
                            Dim area As Double = 0
                            Dim size As Double = 0
                    
                            With New Foo(5, 6).Execute()
                                a = .A
                                b = .B
                                c = .C
                                d = .D
                                e = .E
                                f = .F
                            End With
                    
                            With New Bar("red", "circle").Execute()
                                length = .Length
                                area = .Area
                                size = .Size
                            End With
                    
                            With New Foo(3, 10).Execute()
                                a = .A
                                b = .B
                                c = .C
                                d = .D
                                e = .E
                                f = .F
                            End With
                    
                            With New Bar("blue", "square").Execute()
                                length = .Length
                                area = .Area
                                size = .Size
                            End With
                        End Sub
                    End Class
                    

                    【讨论】:

                      【解决方案17】:

                      C# 9 及以上版本:

                      with 关键字终于添加了!

                      它只支持record 类型。

                      用法

                      with 关键字左侧的实例被复制,其右侧提到的所有字段都被相应的值修改以返回一个新实例。

                      示例

                      假设我们有以下record 类型:

                      public record Model(string Prefix, string Location, bool CoolLocation, bool HasCitizens);
                      

                      我们初始化了以下原始模型:

                      var model = new Model("Hello", "World", true, true);
                      

                      我们想创建一个新模型,除了LocationHasCitizens 之外的所有字段都相同。 我们可以通过以下方式做到这一点:

                      var model2 = model with { Location = "Mars", HasCitizens = false };
                         // Prefix = "Hello"
                         // Location = "Mars"
                         // CoolLocation = true
                         // HasCitizens = false
                      

                      C# 10 及以上:

                      with 关键字现在还可以用于 structanonymous 类型。

                      with 关键字的更多信息可以在 official docs 中找到

                      【讨论】:

                        【解决方案18】:

                        嗯。我从来没有深入使用过 VB.net,所以我在这里做一个假设,但我认为“使用”块可能接近你想要的。

                        using 定义了一个变量的块作用域,见下面的例子

                        using ( int temp = someFunction(param1) ) {
                           temp++;  // this works fine
                        }
                        
                        temp++; // this blows up as temp is out of scope here and has been disposed
                        

                        Here is an article from Microsoft that explains a bit more


                        编辑:是的,这个答案是错误的——最初的假设是不正确的。 VB 的“WITH”更像是新的 C# 对象初始化器:

                        var yourVariable = new yourObject { param1 = 20, param2 = "some string" };
                        

                        【讨论】:

                        • 不,using 语句非常不同 - using 语句的目的是在块的末尾处理资源。它不会使引用值更短。
                        • 谢谢乔恩,学习另一种语言的新东西总是很好,我想我应该注意旧的说法“假设你和我都是混蛋” - 但我想只有我在这种情况下看起来很糟糕;)
                        • 不……初始化器只在初始化时起作用……看看流利的接口!
                        • 没有理由using 不能使引用值更短(借用上面的答案):using (var c=cell.Border(xlEdgeTop)) { c.LineStyle = xlContinuous; c.Weight = xlMedium; c.ColorIndex = xlAutomatic; }
                        【解决方案19】:

                        如果有多个级别的对象,您可以使用“使用”指令获得类似的功能:

                        using System;
                        using GenderType = Hero.GenderType; //This is the shorthand using directive
                        public partial class Test : System.Web.UI.Page
                        {
                            protected void Page_Load(object sender, EventArgs e)
                            {
                                var myHero = new Hero();
                                myHero.Name = "SpamMan";
                                myHero.PowerLevel = 3;
                                myHero.Gender = GenderType.Male; //instead of myHero.Gender = Hero.GenderType.Male;
                            }
                        }
                        public class Hero
                        {
                            public enum GenderType
                            {
                                Male,
                                Female,
                                Other
                            }
                            public string Name;
                            public int PowerLevel;
                            public GenderType Gender;
                        }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2014-11-08
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-06-19
                          • 2010-10-05
                          • 1970-01-01
                          • 2011-04-02
                          • 2021-10-15
                          相关资源
                          最近更新 更多