关于.net:在C#中使用块等效?

With block equivalent in C#?

我知道vb.net,正在尝试更新我的C。C中是否有一个与块等效的?

谢谢


虽然C没有任何与一般情况直接等效的方法,但C 3获得了构造函数调用的对象初始值设定项语法:

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

更多详细信息,请参阅C第8章-您可以从曼宁的网站免费下载。

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


这就是Visual C程序管理器必须说的:为什么C没有"with"声明?

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.


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

通过使用扩展方法和泛型,您可以通过添加如下内容,创建类似于WITH语句的内容:

1
2
3
4
5
    public static T With<T>(this T item, Action<T> action)
    {
        action(item);
        return item;
    }

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

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

对此:

1
2
3
4
5
    updateRoleFamily.With(rf =>
          {
              rf.RoleFamilyDescription = roleFamilyDescription;
              rf.RoleFamilyCode = roleFamilyCode;
          });

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


不,没有。


在"使用对象"部分的页面下方约3/4处:

VB:

1
2
3
4
With hero
  .Name ="SpamMan"
  .PowerLevel = 3
End With

C:

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

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

关于这一点的大讨论如下:

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


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

1
ref MySubClassType e = ref MyMainClass.MySubClass;

然后您可以使用如下快捷方式:用e.property代替MyMainClass.MySubClass.property


最简单的语法是:

1
2
3
4
5
6
7
8
9
10
11
{
    var where = new MyObject();
    where.property ="xxx";
    where.SomeFunction("yyy");
}

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

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


这里是江户十一〔0〕的大粉丝!

这是我目前的C代码:

1
2
3
4
5
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中,它可以是:

1
2
3
4
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项目经理忽略了一些事情。

顺便说一下,这种情况并不常见,但我有时会用到:

而不是

1
2
3
4
5
6
7
Using oClient As HttpClient = New HttpClient
    With oClient
        .BaseAddress = New Uri("http://mysite")
        .Timeout = New TimeSpan(123)
        .PostAsync( ... )
    End With
End Using

你可以使用

1
2
3
4
5
With New HttpClient
    .BaseAddress = New Uri("http://mysite")
    .Timeout = New TimeSpan(123)
    .PostAsync( ... )
End With

你要冒着打手腕的危险-我也是!-但似乎你得到了Using声明在处理等方面的所有好处,而没有额外的规则。

注意:这可能偶尔会出错,所以只能用于非关键代码。或者根本没有。记住:你有选择…


我用这种方式:

1
2
3
4
5
6
7
8
9
        worksheet.get_Range(11, 1, 11, 41)
            .SetHeadFontStyle()
            .SetHeadFillStyle(45)
            .SetBorders(
                XlBorderWeight.xlMedium
                , XlBorderWeight.xlThick
                , XlBorderWeight.xlMedium
                , XlBorderWeight.xlThick)
            ;

setHeadFontStyle/setHeadFillStyle是如下范围的extMethod:

1
2
3
4
5
 public static Range SetHeadFillStyle(this Range rng, int colorIndex)
 {
     //do some operation
     return rng;
 }

执行一些操作并返回下一个操作的范围

它看起来像Linq:)

但现在仍然不能完全像它--属性设定值

1
2
3
4
with cell.Border(xlEdgeTop)
   .LineStyle = xlContinuous
   .Weight = xlMedium
   .ColorIndex = xlAutomatic

有时,您可以通过执行以下操作来摆脱困境:

1
2
3
4
5
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的代码示例)


我认为"with"的壁橱是static using,但只适用于static的方法或属性。例如

1
2
3
4
5
6
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


还有一个有趣的with模式实现

1
2
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;

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

使用的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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);
}

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

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

可能在With方法之后应用,因为默认情况下将返回未修改的结构副本

1
2
3
4
5
6
7
8
9
10
11
12
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);

喜欢的话就尽情享受吧!


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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,所以我在这里做了一个假设,但我认为"使用"块可能接近你想要的。

使用定义变量的块范围,请参见下面的示例

1
2
3
4
5
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对象初始化器:

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