复制和更新功能吗

复制和更新功能吗

本文介绍了我们可以从c#中访问F#复制和更新功能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如在F#中我们可以定义

For example in F# we can define

type MyRecord = {
    X: int;
    Y: int;
    Z: int
    }

let myRecord1 = { X = 1; Y = 2; Z = 3; }

要更新它,我可以做

let myRecord2 = { myRecord1 with Y = 100; Z = 2 }

那是很了不起的事,而事实证明,记录无需任何额外的努力即可自动实现IStructuralEquality,这使我希望在C#中做到这一点.但是,也许我可以在F#中定义记录,但仍然能够在C#中执行一些更新.我想像一个API

That's brilliant and the fact that records automatically implement IStructuralEquality with no extra effort makes me wish for this in C#. However Perhaps I can define my records in F# but still be able to perform some updates in C#. I imagine an API like

MyRecord myRecord2 = myRecord
    .CopyAndUpdate(p=>p.Y, 10)
    .CopyAndUpdate(p=>p.Z, 2)

是否有办法(如上所述)实现CopyAndUpdate? CopyAndUpdate的C#签名为

Is there a way, and I don't mind dirty hacks, to implement CopyAndUpdate as above? The C# signiture for CopyAndUpdate would be

T CopyAndUpdate<T,P>
   ( this T
   , Expression<Func<T,P>> selector
   , P value
   )

推荐答案

可以做到,但是正确做到这一点将非常困难(而且绝对不适合我的回答).以下简单实现假定您的对象仅具有读写属性和无参数构造函数:

It can be done, but doing that properly is going to be quite hard (and it definitely won't fit in my answer). The following simple implementation assumes that your object has only read-write properties and parameter-less constructor:

class Person
{
  public string Name { get; set; }
  public int Age { get; set; }
}

这有点不对劲,因为您可能想在不可变类型上使用它-但随后您总是必须用所有参数调用构造函数,并且不清楚如何链接构造函数参数(当您创建一个实例)以及您可以读取的属性.

This slightly defeats the point, because you would probably want to use this on immutable types - but then you always have to call the constructor with all the arguments and it is not clear how to link the constructor parameters (when you create an instance) with the properties that you can read.

With方法创建一个新实例,复制所有属性值,然后设置要更改的值(使用从表达式树中提取的PropertyInfo-无需任何检查!)

The With method creates a new instance, copies all property values and then sets the one that you want to change (using the PropertyInfo extracted from the expression tree - without any checking!)

public static T With<T, P>(this T self, Expression<Func<T, P>> selector, P newValue)
{
  var me = (MemberExpression)selector.Body;
  var changedProp = (System.Reflection.PropertyInfo)me.Member;

  var clone = Activator.CreateInstance<T>();
  foreach (var prop in typeof(T).GetProperties())
    prop.SetValue(clone, prop.GetValue(self));

  changedProp.SetValue(clone, newValue);
  return clone;
}

以下演示的行为符合预期,但正如我所说,它有很多局限性:

The following demo behaves as expected, but as I said, it has lots of limitations:

var person = new Person() { Name = "Tomas", Age = 1 };
var newPerson = person.With(p => p.Age, 20);

通常,我认为在这里使用像With这样的基于通用反射的方法可能不是一个好主意,除非您有足够的时间来正确实现它.对于您使用的每种类型,只使用一个With方法可能会更容易,该方法采用可选参数并将其值设置为克隆值(手动创建)(如果值不是null).签名将类似于:

In general, I think using a universal reflection-based method like With here might not be such a good idea, unless you have lots of time to implement it properly. It might be easier to just implement one With method for every type that you use which takes optional parameters and sets their values to a cloned value (created by hand) if the value is not null. The signature would be something like:

public Person With(string name=null, int? age=null) { ... }

这篇关于我们可以从c#中访问F#复制和更新功能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 00:48