与C#中的块等效吗?


72

我知道VB.Net,正在尝试重新学习C#。C#中是否有一个With块等效项?

谢谢


1
我知道在SO上有一个重复,但是对于我一生来说,我无法找到可以找到它的搜索查询。
ctacke

6
@ctacke:我认为“不能那么难......”,然后花了10分钟未果试图找到它!....
米奇小麦

谷歌知道: “用C#网站:stackoverflow.com”给出:stackoverflow.com/questions/1175334/...stackoverflow.com/questions/1063429/...
schoetbi

Answers:


27

这就是Visual C#程序管理器必须说的: 为什么C#没有'with'语句?

许多人,包括C#语言设计师在内,都认为“ with”通常会损害可读性,并且更多的是诅咒而不是祝福。声明具有有意义名称的局部变量,并使用该变量对单个对象执行多个操作要比拥有一个带有某种隐式上下文的块更为清楚。


7
无效链接。我认为这是它的新景点:blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx
Keith

4
链接又死了。下面是从C#帮助一个新问题:blogs.msdn.com/b/csharpfaq/archive/2004/03/11/...
KyleMit

3
完全不同意。我不能格式化代码示例,我想在一个评论,但现在我有一个整体get与散落SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization代替With SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization { .AuthToken = "123" .RefreshToken = "456" ... }
SteveCinq '18年

除非您使用的是一个非常不寻常的库,否则没有理由不能缓存授权对象。var auth = SKYLib.AccountsPayable.Client.ApiAuthorization.Authorization; auth.AuthToken = "123"; auth.RefreshToken = "456"; ... 。换句话说,可以通过准确地执行您抱怨的处方问题来解决您的问题。
snarf

您应该始终为变量赋予有意义的名称。但是,如果您可以为新创建的对象创建单独的作用域,则有时会很有用。{ var o = new object(); o.use (); ..}到目前为止,手动创建该范围是唯一可行的方法。
Tobias Knauss

57

尽管C#在一般情况下没有任何直接等效项,但是C#3为构造函数调用获取对象初始化器语法:

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

有关更多详细信息,请参见C#中的第8章-您可以从以下位置免费下载它 Manning的网站

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


1
这一点根本不一样,因为这涉及创建类的实例,而With块则不同。
John Stock

3
@JohnStock:因此“在一般情况下没有直接等价物”。With但是,在仅使用with语句进行初始化的情况下,它仍然是相应的习惯用法。
乔恩·斯基特

这个初始化签名主要是Lambda表达式实现的产物,而不是其他任何东西。它是C#中最接近VB的With简写的签名。
GoldBishop

@GoldBishop:它是独立的lambda表达式的-但两者的建立是为了支持LINQ在C#3
乔恩斯基特

1
@ maf-soft:可以在github.com/dotnet/csharplang那里,但是我看不到它的发生。我可以想象with被用于不可变类型,而不是普通的VB情况。
乔恩·斯基特

22

正如上面链接的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;
          });

在这样的示例中,唯一的好处也许是布局更好,但是具有更复杂的引用和更多属性,它很可能使您的代码更具可读性。


4
我并没有真正看到您的示例所显示的优势。原始代码(pre-lambda)是[objectinstance]。[property] = [value]。Lambda代码基本上只是使用rf更改updateRoleFamily。
丹·苹果亚德

3
尝试使用对对象实例和更多属性的更长引用。在上面的示例中,您将updateRoleFamily简化为rf并设置了两个正确的属性,这并不是很大的收获。但是,如果您的对象实例类似于myDataStructure.GetButton(44),并且您必须设置十个属性,则使用lambda或设置局部变量可能使其更具可读性。就像原始的VB With语句一样,您可以选择或离开一点语法糖。
RTPeat

4
@DanAppleyard我将其简单地看做封装,有时可能非常有用。但是后来我就产生了偏见,因为来自VB.NET的With经常派上用场,而我真的在C#中错过了它。完全不熟悉C#的任何人都不会出现可读性问题,只是使编码变得更加容易,特别是。处理长对象名称时。
ingredient_15939

5
似乎设置局部变量会更有效且更易读。var rf = myDataStructure.GetButton(44);我根本看不到这是语法糖。这只是设置局部变量的复杂方法。“仅仅因为你做就意味着你不应该做。”
2015年


12

在“使用对象”部分的页面下约3/4 :

VB:

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

C#:

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



4

最简单的语法是:

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

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

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


尽管我确实希望他们最终添加一个With块,但是我喜欢这种方法,因为它不需要任何额外的库或自定义代码,并且可以成功地防止对象在范围外发生突变。
Merkle Groot

3

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

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

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

更新:

在撰写此答案时,C#9尚未正式发布,而仅在预览中。但是,计划于2020年11月与.NET 5.0一起交付。

有关更多信息,请检查记录类型


1

有时您可以执行以下操作:

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


1

我正在使用这种方式:

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

SetHeadFontStyle / SetHeadFillStyle是范围的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

1

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从处理等方面报表的,而无需额外的琐事。

注意:这有时可能会出错,因此仅将其用于非关键代码。还是根本没有。记住:您可以选择...



0

另一种有趣的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;

您可以通过该链接浏览更多详细信息并研究在线代码示例

用法变化

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

尽情享受吧!



0

对我来说,我试图自动生成代码,并且需要为多个不同的类重用一个简单的变量,如“ 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

-3

嗯。我从未深度使用过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

这是微软的文章,解释得更多


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

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

不,using语句有很大的不同-using语句的目的是在块的末尾处置资源。它不会使引用值变得更短。
乔恩·斯基特

谢谢乔恩,总是很高兴学到另一门语言的新知识,我想我应该听从旧的陈述“假设让你和我变得愚蠢”-但我想在这种情况下只有我看起来很糟糕;)
mlennox 09年

2
不,...初始化程序仅适用于初始化...看看流利的接口!
Andreas Niedermair,2009年

没有理由using不能使该值更短(从上面的答案中借来):using (var c=cell.Border(xlEdgeTop)) { c.LineStyle = xlContinuous; c.Weight = xlMedium; c.ColorIndex = xlAutomatic; }
NetMage '16

-3

如果存在多个级别的对象,则可以使用“ using”指令获得相似的功能:

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.