C#:如何在F#中将扩展方法定义为"with"?

ath*_*hos 8 c# extension-methods f# immutability with-statement

F#有一个方便的功能"with",例如:

type Product = { Name:string; Price:int };;
let p = { Name="Test"; Price=42; };;
let p2 = { p with Name="Test2" };;
Run Code Online (Sandbox Code Playgroud)

F#创建关键字"with"作为记录类型默认是不可变的.

现在,是否可以在C#中定义类似的扩展?看起来有点棘手,就像在C#中我不确定如何转换字符串

Name="Test2"
Run Code Online (Sandbox Code Playgroud)

代表或表达?

Dan*_*iel 4

public static T With<T, U>(this T obj, Expression<Func<T, U>> property, U value)
    where T : ICloneable {
    if (obj == null)
        throw new ArgumentNullException("obj");
    if (property == null)
        throw new ArgumentNullException("property");
    var memExpr = property.Body as MemberExpression;
    if (memExpr == null || !(memExpr.Member is PropertyInfo))
        throw new ArgumentException("Must refer to a property", "property");
    var copy = (T)obj.Clone();
    var propInfo = (PropertyInfo)memExpr.Member;
    propInfo.SetValue(copy, value, null);
    return copy;
}

public class Foo : ICloneable {
    public int Id { get; set; } 
    public string Bar { get; set; }
    object ICloneable.Clone() {
        return new Foo { Id = this.Id, Bar = this.Bar };
    }
}

public static void Test() {
    var foo = new Foo { Id = 1, Bar = "blah" };
    var newFoo = foo.With(x => x.Bar, "boo-ya");
    Console.WriteLine(newFoo.Bar); //boo-ya
}
Run Code Online (Sandbox Code Playgroud)

或者,使用复制构造函数:

public class Foo {
    public Foo(Foo other) {
        this.Id = other.Id;
        this.Bar = other.Bar;
    }
    public Foo() { }
    public int Id { get; set; } 
    public string Bar { get; set; }
}

public static void Test() {
    var foo = new Foo { Id = 1, Bar = "blah" };
    var newFoo = new Foo(foo) { Bar = "boo-ya" };
    Console.WriteLine(newFoo.Bar);
}
Run Code Online (Sandbox Code Playgroud)

乔治的出色建议略有不同,允许进行多项作业:

public static T With<T>(this T obj, params Action<T>[] assignments)
    where T : ICloneable {
    if (obj == null)
        throw new ArgumentNullException("obj");
    if (assignments == null)
        throw new ArgumentNullException("assignments");
    var copy = (T)obj.Clone();
    foreach (var a in assignments) {
        a(copy);
    }
    return copy;
}

public static void Test() {
    var foo = new Foo { Id = 1, Bar = "blah" };
    var newFoo = foo.With(x => x.Id = 2, x => x.Bar = "boo-ya");
    Console.WriteLine(newFoo.Bar);
}
Run Code Online (Sandbox Code Playgroud)

我可能会使用第二个,因为(1)任何通用解决方案都将变得不必要的缓慢和复杂;(2) 它具有最接近您想要的语法(并且该语法符合您的期望);(3) F# 复制和更新表达式的实现类似。

  • @athos:...同时,我推荐 F#。:-) (2认同)