gpt4 book ai didi

c# - 使用反射实例化不可变对象(immutable对象)

转载 作者:太空狗 更新时间:2023-10-29 22:18:59 27 4
gpt4 key购买 nike

我创建了一个基类来帮助我减少 C# 中不可变对象(immutable对象)初始化的样板代码,

我使用惰性初始化是为了尽量不对性能产生太大影响,我想知道这样做会对性能产生多大影响?

这是我的基类:

public class ImmutableObject<T>
{
private readonly Func<IEnumerable<KeyValuePair<string, object>>> initContainer;

protected ImmutableObject() {}

protected ImmutableObject(IEnumerable<KeyValuePair<string,object>> properties)
{
var fields = GetType().GetFields().Where(f=> f.IsPublic);

var fieldsAndValues =
from fieldInfo in fields
join keyValuePair in properties on fieldInfo.Name.ToLower() equals keyValuePair.Key.ToLower()
select new {fieldInfo, keyValuePair.Value};

fieldsAndValues.ToList().ForEach(fv=> fv.fieldInfo.SetValue(this,fv.Value));

}

protected ImmutableObject(Func<IEnumerable<KeyValuePair<string,object>>> init)
{
initContainer = init;
}

protected T setProperty(string propertyName, object propertyValue, bool lazy = true)
{

Func<IEnumerable<KeyValuePair<string, object>>> mergeFunc = delegate
{
var propertyDict = initContainer == null ? ObjectToDictonary () : initContainer();
return propertyDict.Select(p => p.Key == propertyName? new KeyValuePair<string, object>(propertyName, propertyValue) : p).ToList();
};

var containerConstructor = typeof(T).GetConstructors()
.First( ce => ce.GetParameters().Count() == 1 && ce.GetParameters()[0].ParameterType.Name == "Func`1");

return (T) (lazy ? containerConstructor.Invoke(new[] {mergeFunc}) : DictonaryToObject<T>(mergeFunc()));
}

private IEnumerable<KeyValuePair<string,object>> ObjectToDictonary()
{
var fields = GetType().GetFields().Where(f=> f.IsPublic);
return fields.Select(f=> new KeyValuePair<string,object>(f.Name, f.GetValue(this))).ToList();
}

private static object DictonaryToObject<T>(IEnumerable<KeyValuePair<string,object>> objectProperties)
{
var mainConstructor = typeof (T).GetConstructors()
.First(c => c.GetParameters().Count()== 1 && c.GetParameters().Any(p => p.ParameterType.Name == "IEnumerable`1") );
return mainConstructor.Invoke(new[]{objectProperties});
}

public T ToObject()
{
var properties = initContainer == null ? ObjectToDictonary() : initContainer();
return (T) DictonaryToObject<T>(properties);
}
}

可以这样实现:

public class State:ImmutableObject<State>
{
public State(){}
public State(IEnumerable<KeyValuePair<string,object>> properties):base(properties) {}
public State(Func<IEnumerable<KeyValuePair<string, object>>> func):base(func) {}

public readonly int SomeInt;
public State someInt(int someInt)
{
return setProperty("SomeInt", someInt);
}

public readonly string SomeString;
public State someString(string someString)
{
return setProperty("SomeString", someString);
}
}

可以这样使用:

//creating new empty object
var state = new State();

// Set fields, will return an empty object with the "chained methods".
var s2 = state.someInt(3).someString("a string");
// Resolves all the "chained methods" and initialize the object setting all the fields by reflection.
var s3 = s2.ToObject();

最佳答案

正如评论中已经提到的,更有意义的是,不要将不可变实例实现或接口(interface)与本质上是新实例的构建器的行为“混为一谈”。

您可以通过这种方式制作更简洁且类型安全的解决方案。所以我们可以定义一些标记接口(interface)并为其键入安全版本:

public interface IImmutable : ICloneable { }
public interface IImmutableBuilder { }

public interface IImmutableOf<T> : IImmutable where T : class, IImmutable
{
IImmutableBuilderFor<T> Mutate();
}

public interface IImmutableBuilderFor<T> : IImmutableBuilder where T : class, IImmutable
{
T Source { get; }
IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, TFieldType value);
IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, Func<T, TFieldType> valueProvider);
IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, TFieldType value);
IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, Func<TFieldType, TFieldType> valueProvider);
T Build();
}

并在如下类中提供所有必需的基本 builder 行为。请注意,为了简洁/简单起见,省略了大多数错误检查/编译委托(delegate)创建。可以在此 gist 中找到具有合理错误检查级别的更清晰、性能优化的版本.

public class DefaultBuilderFor<T> : IImmutableBuilderFor<T> where T : class, IImmutableOf<T>
{
private static readonly IDictionary<string, Tuple<Type, Action<T, object>>> _setters;
private List<Action<T>> _mutations = new List<Action<T>>();

static DefaultBuilderFor()
{
_setters = GetFieldSetters();
}

public DefaultBuilderFor(T instance)
{
Source = instance;
}

public T Source { get; private set; }

public IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, TFieldType value)
{
// Notes: error checking omitted & add what to do if `TFieldType` is not "correct".
_mutations.Add(inst => _setters[fieldName].Item2(inst, value));
return this;
}

public IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, Func<T, TFieldType> valueProvider)
{
// Notes: error checking omitted & add what to do if `TFieldType` is not "correct".
_mutations.Add(inst => _setters[fieldName].Item2(inst, valueProvider(inst)));
return this;
}

public IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, TFieldType value)
{
// Error checking omitted.
var memberExpression = fieldExpression.Body as MemberExpression;
return Set<TFieldType>(memberExpression.Member.Name, value);
}

public IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, Func<TFieldType, TFieldType> valueProvider)
{
// Error checking omitted.
var memberExpression = fieldExpression.Body as MemberExpression;
var getter = fieldExpression.Compile();
return Set<TFieldType>(memberExpression.Member.Name, inst => valueProvider(getter(inst)));
}

public T Build()
{
var result = (T)Source.Clone();
_mutations.ForEach(x => x(result));
return result;
}

private static IDictionary<string, Tuple<Type, Action<T, object>>> GetFieldSetters()
{
// Note: can be optimized using delegate setter creation (IL).
return typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(x => !x.IsLiteral)
.ToDictionary(
x => x.Name,
x => SetterEntry(x.FieldType, (inst, val) => x.SetValue(inst, val)));
}

private static Tuple<Type, Action<T, object>> SetterEntry(Type type, Action<T, object> setter)
{
return Tuple.Create(type, setter);
}
}

示例用法

然后可以这样使用,使用您的 State 示例类:

public static class Example
{
public class State : IImmutableOf<State>
{
public State(int someInt, string someString)
{
SomeInt = someInt;
SomeString = someString;
}

public readonly int SomeInt;
public readonly string SomeString;

public IImmutableBuilderFor<State> Mutate()
{
return new DefaultBuilderFor<State>(this);
}

public object Clone()
{
return base.MemberwiseClone();
}

public override string ToString()
{
return string.Format("{0}, {1}", SomeInt, SomeString);
}
}

public static void Run()
{
var original = new State(10, "initial");

var mutatedInstance = original.Mutate()
.Set("SomeInt", 45)
.Set(x => x.SomeString, "Hello SO")
.Build();
Console.WriteLine(mutatedInstance);

mutatedInstance = original.Mutate()
.Set(x => x.SomeInt, val => val + 10)
.Build();
Console.WriteLine(mutatedInstance);
}
}

输出如下:

45, Hello SO
20, initial

关于c# - 使用反射实例化不可变对象(immutable对象),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24395241/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com