C#到C#,方便的语言特性

11

我想学习C#的所有便利特性以及它们如何映射到C#。

例如,自动属性:

public string Foo { get; set; }

...转换为类似这样的内容:

string <Foo>k__BackingField;

[CompilerGenerated]
public string Foo {
   get { return this.<Foo>k__BackingField; } 
   set { this.<Foo>k__BackingField = value; } 
}

foreach循环:

foreach(char c in "Hello") {
   Console.WriteLine(c);
}

...映射到这种形式(我想):

CharEnumerator en;

try {

   en = "Hello".GetEnumerator();

   while (en.MoveNext()) {
      char c = en.Current;

      Console.WriteLine(c);
   }

} finally {

   IDisposable disp = en as IDisposable;

   if (disp != null)
      disp.Dispose();
}

使用枚举器进行释放可以使foreach在处理非托管资源时非常有用,例如循环遍历文件中的行或数据库中的记录。

我认为对这些高级功能的充分理解可以帮助我们编写更好的代码。C#还有哪些方便的功能,它们与C#的映射方式是什么?


1
你是在问C#如何映射到C#吗? - Kris Krause
看起来maxtoroq想要收集一些关于如何在纯C#中扩展一些“便利”功能的信息。谁将展示using(){}结构? :) try-finally,或者在finally部分调用Dispose之前检查null。对吗? - IgorK
6
实际上,在foreach循环中,重要的是将char c放在循环外部,以解释臭名昭著的捕获问题。 - Marc Gravell
1
这个foreach循环真的被转换成那个了吗?天哪,是的,现在我们可以编辑注释了! - Allen Rice
8
规范文档描述了所有映射到低级构造的高级构造。 - Eric Lippert
14个回答

11

如果你想讨论节省的代码量,我认为最大的节省者是:

迭代器块

示例:

public static IEnumerable<int> Get() {yield return 1; yield return 2;}

保存:

  • 编写一个 IEnumerable<T> 实现
  • 编写一个 IEnumerator<T> 实现
  • 处理枚举器状态;你看到了它有多糟糕吗?说真的。

捕获变量(匿名方法/lambda表达式)

示例:

var list2 = list1.FindAll(x => (x.Value % 10) == find);

已保存:

  • 编写一个类来保存状态
    • 在复杂情况下,嵌套的状态
  • 初始化状态包装器
  • 在该类上编写一个方法

表达式lambda编译器

示例(源自上文):

Expression<Func<Foo,bool>> pred = x => (x.Value % 10) == find;

已保存:

  • yeuch - 大量树形代码;非常复杂;在上面的代码中我可以看到(没有特定的顺序)Expression.ParameterExpression.PropertyExpression.FieldExpression.ConstantExpression.EqualExpression.ModuloExpression.Lambda... 组合起来就是;-p

8
使用using是一种语法糖。
using(x) 
{
    ...
}

映射到

try {
    // Code
}
finally
{
    if(x != null)
        ((IDisposable)x).Dispose();
}

LINQ中的from关键字是语法糖。
from x in l where y select z;

翻译成

l.Where(x => y).Select(x => z);

这很接近但不完全正确。实际上它调用了((IDisposable)x).Dispose()。 - womp

6

LINQ作为一个整体(尤其是使用LINQ查询语法),基本上是C#/.NET的一个重要便利功能,可以在很多时候替换for循环/迭代器/复杂查询逻辑。


5

4

?: 运算符用于测试布尔值或布尔表达式,如果测试结果为true,则返回第一个值,否则返回第二个值:

condition ? first_expression : second_expression

这基本上意味着:

var x;
if (condition)
{
    x = first_expression;
}
else
{
    x = second_expression
}

使用 ?: 运算符,你可以写成 int x = condition ? 10 : 20; 而不是必须要写整个 if 语句。

3

对象初始化器是一种简洁的语法糖:

 public class Book
{
 public string Author { get; set; }
 public string Title { get; set; }
}

Book book = new Book() {Author = "John Smith", Title = "C# Syntax"};

以上是另一种书写方式:
Book book = new Book();
book.Author = "John Smith";
book.Title = "C# Syntax";

隐式类型:

var book = new Book() {Author = "John Smith", Title = "C# Syntax"};

不使用对象初始化器,这如何映射到C#? - Max Toro
1
在构造函数调用之后逐个调用setter应该很明显。 - IgorK
顺便说一下,“匿名类型”示例看起来不对,因为那里有一个普通的Book类型。这是打字错误吗?应该改成var book = new {Author = "John Smith", Title = "C# Syntax"}; 吗? :) - IgorK
1
那不是匿名类型示例,它是隐式类型。 - user47589

3

空值合并运算符(??)非常实用。它可以在变量为null时提供一个默认值,避免了繁琐的判断语句。

ISomeRef myRef = firstChoice ?? secondChoice ?? fallback;

翻译成相当于以下功能的内容:

ISomeRef myRef = firstChoice;
if(myRef == null) myRef = secondChoice;
if(myRef == null) myRef = fallback;

我说“功能等效”,因为我还没有在反编译工具中查看代码 :)

3

匿名类型:

var message = new { Message = "Hello, world!", MessageID = 1 };

被翻译为

<>f__AnonymousType0<string, int> message =
    new <>f__AnonymousType0<string, int>("Hello, world!", 1);

在哪里

[CompilerGenerated]
[DebuggerDisplay(
    @"\{ Message = {Message},
    MessageID = {MessageID} }",
    Type="<Anonymous Type>"
 )
]
internal sealed class <>f__AnonymousType0<
    <Message>j__TPar,
    <MessageID>j__TPar
> {
    // Fields
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly <Message>j__TPar <Message>i__Field;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly <MessageID>j__TPar <MessageID>i__Field;

    // Methods
    [DebuggerHidden]
    public <>f__AnonymousType0(
        <Message>j__TPar Message,
        <MessageID>j__TPar MessageID
    ) {
        this.<Message>i__Field = Message;
        this.<MessageID>i__Field = MessageID;
    }

    [DebuggerHidden]
    public override bool Equals(object value) {
        var type = value as <>f__AnonymousType0<
            <Message>j__TPar,
            <MessageID>j__TPar
        >;
        return (((type != null) && 
            EqualityComparer<<Message>j__TPar>.Default.Equals(
                this.<Message>i__Field, 
                type.<Message>i__Field)
            ) &&
            EqualityComparer<<MessageID>j__TPar>.Default.Equals(
                this.<MessageID>i__Field,
                type.<MessageID>i__Field)
            );
    }

    [DebuggerHidden]
    public override int GetHashCode() { 
        int num = 0x2e22c70c;
        num = (-1521134295 * num) +
            EqualityComparer<<Message>j__TPar>.Default.GetHashCode(
                this.<Message>i__Field
            );
        return ((-1521134295 * num) +
            EqualityComparer<<MessageID>j__TPar>.Default.GetHashCode(
                this.<MessageID>i__Field)
            );
    }

    [DebuggerHidden]
    public override string ToString() {
        StringBuilder builder = new StringBuilder();
        builder.Append("{ Message = ");
        builder.Append(this.<Message>i__Field);
        builder.Append(", MessageID = ");
        builder.Append(this.<MessageID>i__Field);
        builder.Append(" }");
        return builder.ToString();
    }

    // Properties
    public <Message>j__TPar Message {
        get {
            return this.<Message>i__Field;
        }
    }

    public <MessageID>j__TPar MessageID {
        get {
            return this.<MessageID>i__Field;
        }
    }
}

3
匿名委托在编译期间会被转换为方法,因此以下代码将被转换为这样的方法:
var collection = new[] { 1, 2, 3, 4 }.Where(i => i == 2);

引起新方法的创建:
[CompilerGenerated]
private static bool <Main>b__0(int i)
{
    return (i == 2);
}

并更改为:

IEnumerable<int> collection = new int[] { 1, 2, 3, 4 }.Where<int>(<Main>b__0);

请注意,它使用了一个在编译之前无法创建的方法名称<Main>b__0 - 你不能在方法名称中使用<>,所以它总是避免冲突。
对于这个过程,i => i == 2也是一样的。
delegate(int i) { return i == 2; }

您还会注意到,您可以使用简写来表示:

new[] { 1, 2, 3, 4 }
// goes to...
new int[] { 1, 2, 3, 4 }

并且

var foo = new Foo();
// goes to...
Foo foo = new Foo();

1

Lambda表达式是另一个例子。

var myZombies = myMonsters.Where(monster => monster.Kind == MonsterKind.Zombie);

将 Lambda 委托提取到具有奇怪名称的自己的方法中。

您可以使用 Reflector 并在选项对话框中切换 C# 版本,以查看解决此类问题的差异。


严格来说,它只对委托类型执行此操作(上述语法也可以与“表达式”类型一起使用,但实现方式完全不同)。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接