使用 C # 中的块等效?

我知道 VB.Net,正在努力复习我的 C # 。 C # 中有 With代码块等价吗?

135083 次浏览

No, there is not.

You could use the argument accumulator pattern.

Big discussion about this here:

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

hmm. I have never used VB.net in any depth, so I'm making an assumption here, but I think the 'using' block might be close to what you want.

using defines a block scope for a variable, see the example below

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


EDIT: yeah, this answer is wrong - the original assumption was incorrect. VB's 'WITH' is more like the new C# object initialisers:

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

About 3/4 down the page in the "Using Objects" section:

VB:

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

C#:

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

Although C# doesn't have any direct equivalent for the general case, C# 3 gain object initializer syntax for constructor calls:

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

See chapter 8 of C# in Depth for more details - you can download it for free from Manning's web site.

(Disclaimer - yes, it's in my interest to get the book into more people's hands. But hey, it's a free chapter which gives you more information on a related topic...)

This is what Visual C# program manager has to say: Why doesn't C# have a 'with' statement?

Many people, including the C# language designers, believe that 'with' often harms readability, and is more of a curse than a blessing. It is clearer to declare a local variable with a meaningful name, and use that variable to perform multiple operations on a single object, than it is to have a block with a sort of implicit context.

As the Visual C# Program Manager linked above says, there are limited situations where the With statement is more efficient, the example he gives when it is being used as a shorthand to repeatedly access a complex expression.

Using an extension method and generics you can create something that is vaguely equivalent to a With statement, by adding something like this:

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

Taking a simple example of how it could be used, using lambda syntax you can then use it to change something like this:

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

To this:

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

On an example like this, the only advantage is perhaps a nicer layout, but with a more complex reference and more properties, it could well give you more readable code.

Sometimes you can get away with doing the following:

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

(Code sample for EPPLus @ http://zeeshanumardotnet.blogspot.com)

I was using this way:

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

SetHeadFontStyle / SetHeadFillStyle is ExtMethod of Range like below:

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

do some operation and return the Range for next operation

it's look like Linq :)

but now still can't fully look like it -- propery set value

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

Most simple syntax would be:

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


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

Actually extra code-blocks like that are very handy if you want to re-use variable names.

If there are multiple levels of objects you can get similar functionality with the "using" directive:

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

There is another an interesting implementation of 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;

You may browse more details by the link and research online code samples.

Variations of usage

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

If you work with structs [value types] the similar extension method will be useful too

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

May be applied after With method because by default will be returned the unmodified copy of 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);

Enjoy if you like!

What I do is use a csharp ref keyword. For example:

ref MySubClassType e = ref MyMainClass.MySubClass;

you can then use the shortcut like: e.property instead of MyMainClass.MySubClass.property

A big fan of With here!

This is literally my current C# code:

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

In VB it could be:

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

Much clearer I think. You could even tweak it to be more concise by tuning the With variable. And, style-wise, I still have a choice! Perhaps something the C# Program Manager has overlooked.

As an aside, it's not very common to see this, but I have used it on occasion:

Instead of

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

You can use

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

You risk a wrist-slapping - as do I for posting! - but it seems that you get all the benefits of a Using statement in terms of disposal, etc without the extra rigmarole.

NOTE: This can go wrong occasionally, so only use it for non-critical code. Or not at all. Remember: You have a choice ...

I think the closets thing to "with" is static using, but only works with static's methods or properties. e.g.

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

More Info: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-static

To make life a little easier you can use vertical selection to quickly edit things

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

For me I was trying to auto generate code, and needed to reuse a simple variable like "x" for multiple different classes so that I didn't have to keep generating new variable names. I found that I could limit the scope of a variable and reuse it multiple times if I just put the code in a curly brace section {}.

See the example:

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

In VB I would have used the With and not needed variable "x" at all. Excluding the vb class definitions of Foo and Bar, the vb code is:

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

The with keywork is introduced in C# version 9! you can use it to create copy of object like follows

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

"The above line creates a new Person record where the LastName property is a copy of person, and the FirstName is "Paul". You can set any number of properties in a with-expression. Any of the synthesized members except the "clone" method may be written by you. If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method."

UPDATE:

At the time this answer is written, C#9 is not officially released but in preview only. However, it is planned to be shipped along with .NET 5.0 in November 2020

for more information, check the record types.

C# 9 and above:

The with keyword has finally been added!

It only supports record types.

Usage

The instance on the left hand side of the with keyword is copied and all of the mentioned fields on its right hand side are modified by their according value to return a new instance.

Example

Suppose we have the following record type:

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

And we have following original model initialized:

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

We want to create a new model with all of the same fields except for Location and HasCitizens. We could do it in the following way:

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

C# 10 and above:

The with keyword can now additionally be used for struct and anonymous types.

More info to the with keyword can be found within the official docs