簡體   English   中英

在 C# 中與塊等效?

[英]With block equivalent in C#?

我知道 VB.Net 並且正在嘗試復習我的 C#。 C# 中是否有等效的With塊?

盡管 C# 對於一般情況沒有任何直接等效項,但 C# 3 為構造函數調用獲得了對象初始值設定項語法:

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

有關更多詳細信息,請參閱 C# 深入了解的第 8 章 - 您可以從Manning 的網站免費下載。

(免責聲明 - 是的,讓更多人掌握這本書符合我的利益。但是,嘿,這是一個免費章節,可為您提供有關相關主題的更多信息......)

這就是 Visual C# 程序管理器必須說的: 為什么 C# 沒有“with”語句?

許多人,包括 C# 語言設計者,都認為“with”通常會損害可讀性,與其說是一種祝福,不如說是一種詛咒。 用有意義的名稱聲明一個局部變量,並使用該變量對單個對象執行多個操作,這比使用具有某種隱式上下文的塊更清晰。

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

在這樣的示例中,唯一的優勢可能是更好的布局,但是有了更復雜的引用和更多的屬性,它可以為您提供更易讀的代碼。

不,沒有。

在“ 使用對象”部分的頁面下方約 3/4:

VB:

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

C#:

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

在 C# 版本 9 中引入了with鍵工作! 您可以使用它來創建對象的副本,如下所示

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

“上面的行創建了一個新的 Person 記錄,其中 LastName 屬性是 person 的副本,FirstName 是“Paul”。您可以在 with 表達式中設置任意數量的屬性。除了“clone”之外的任何合成成員方法可能由您編寫。如果記錄類型具有與任何合成方法的簽名匹配的方法,則編譯器不會合成該方法。”

更新:

在撰寫此答案時,C#9 尚未正式發布,而只是預覽版。 但是,它計划在 2020 年 11 月與 .NET 5.0 一起發布

有關更多信息,請查看記錄類型

最簡單的語法是:

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

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

實際上,如果您想重用變量名,像這樣的額外代碼塊非常方便。

我所做的是使用 csharp ref 關鍵字。 例如:

ref MySubClassType e = ref MyMainClass.MySubClass;

然后你可以使用這樣的快捷方式: e.property而不是MyMainClass.MySubClass.property

您可以使用參數累加器模式。

關於這里的大討論:

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

有時,您可以通過執行以下操作來逃脫懲罰:

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

我是這樣使用的:

        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

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語句的所有好處,而無需額外的繁瑣。

注意:這偶爾會出錯,所以只將它用於非關鍵代碼。 或者根本沒有。 記住:你有一個選擇......

為了讓生活更輕松,您可以使用垂直選擇來快速編輯內容

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

還有一個有趣的 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);

喜歡就盡情享受吧!

我認為“with”的壁櫥是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

對我來說,我試圖自動生成代碼,並且需要為多個不同的類重用像“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

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和新添加的record struct類型。

更多關於with關鍵字的信息可以在官方文檔中找到

如果有多個級別的對象,您可以使用“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;
}

嗯。 我從來沒有深入使用過 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" };

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM