Skip to content

Instantly share code, notes, and snippets.

@jtheisen
Created January 25, 2019 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtheisen/5437c5e8a77f409769f8a47da835d103 to your computer and use it in GitHub Desktop.
Save jtheisen/5437c5e8a77f409769f8a47da835d103 to your computer and use it in GitHub Desktop.
Properties Utility
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Properties.Internals
{
public class PropertyPairHelper<T, S>
{
public static PropertyPairHelper<T, S> dummy = new PropertyPairHelper<T, S>();
public IPropertyMapping<T, S, P> By<P>(
Expression<Func<T, P>> target,
Expression<Func<S, P>> source
)
{
return PropertyPair.Get(target, source);
}
public class ArrayBuilder
{
List<IPropertyMapping<T, S>> list = new List<IPropertyMapping<T, S>>();
public void Add<P>(Expression<Func<T, P>> target, Expression<Func<S, P>> source)
{
list.Add(PropertyPair.Get(target, source));
}
internal IPropertyMapping<T, S>[] Build() => list.ToArray();
}
public IPropertyMapping<T, S>[] BuildArray(Action<ArrayBuilder> action)
{
var builder = new ArrayBuilder();
action(builder);
return builder.Build();
}
}
public class PropertyHelper<C>
{
public static PropertyHelper<C> dummy = new PropertyHelper<C>();
public Property<C, P> By<P>(Expression<Func<C, P>> expression)
{
var info = Property.GetInfo(expression);
return new Property<C, P>(info);
}
}
}
namespace Properties
{
using Internals;
public class PropertyPair
{
public static PropertyPairHelper<T, S> Of<T, S>() => PropertyPairHelper<T, S>.dummy;
public static IPropertyMapping<T, S, P> Get<T, S, P>(
Expression<Func<T, P>> target, Expression<Func<S, P>> source
) =>
new PropertyMapping<T, S, P>(
Property.Get(target), Property.Get(source)
);
}
public class Property
{
public static PropertyHelper<C> Of<C>()
{
return PropertyHelper<C>.dummy;
}
public static Property<C, P> Get<C, P>(Expression<Func<C, P>> expression)
{
var info = GetInfo(expression);
return new Property<C, P>(info);
}
public static PropertyInfo GetInfo<C>(Expression<Func<C, Object>> expression)
{
return GetInfo(expression);
}
public static PropertyInfo GetInfo<C, P>(Expression<Func<C, P>> expression)
{
MemberExpression exp = null;
//this line is necessary, because sometimes the expression comes in as Convert(originalExpression)
if (expression.Body is UnaryExpression)
{
var UnExp = (UnaryExpression)expression.Body;
if (UnExp.Operand is MemberExpression)
{
exp = (MemberExpression)UnExp.Operand;
}
else
throw new ArgumentException();
}
else if (expression.Body is MemberExpression)
{
exp = (MemberExpression)expression.Body;
}
else
{
throw new ArgumentException();
}
return (PropertyInfo)exp.Member;
}
}
public interface IProperty<in C>
{
Object UntypedGet(C target);
void UntypedSet(C target, Object value);
Boolean IsDefault(C target);
}
public interface IProperty<in C, P> : IProperty<C>
{
P Get(C target);
void Set(C target, P value);
}
public class Property<C, P> : IProperty<C, P>
{
private readonly PropertyInfo info;
internal Property(PropertyInfo info)
{
this.info = info;
}
public P Get(C target) => (P)info.GetValue(target);
public void Set(C target, P value) => info.SetValue(target, value);
public override Boolean Equals(Object obj)
{
var other = obj as Property<C, P>;
return info.Equals(other?.info);
}
public override Int32 GetHashCode()
{
return info.GetHashCode();
}
Object IProperty<C>.UntypedGet(C target) => Get(target);
void IProperty<C>.UntypedSet(C target, Object value) => Set(target, (P)value);
Boolean IProperty<C>.IsDefault(C target) => Object.Equals(Get(target), default(P));
}
public interface IPropertyMapping<in T, in S>
{
IProperty<T> Target { get; }
IProperty<S> Source { get; }
void Assign(T target, S source);
void AssignReverse(S target, T source);
Boolean AreEqual(T target, S source);
}
public interface IPropertyMapping<in T, in S, P> : IPropertyMapping<T, S>
{
}
public class PropertyMapping<T, S, P> : IPropertyMapping<T, S, P>
{
public PropertyMapping(Property<T, P> target, Property<S, P> source)
{
Target = target;
Source = source;
}
public Property<T, P> Target { get; }
public Property<S, P> Source { get; }
IProperty<T> IPropertyMapping<T, S>.Target => Target;
IProperty<S> IPropertyMapping<T, S>.Source => Source;
public void Assign(T target, S source)
{
Target.Set(target, Source.Get(source));
}
public void AssignReverse(S target, T source)
{
Source.Set(target, Target.Get(source));
}
public Boolean AreEqual(T target, S source) => Object.Equals(Target.Get(target), Source.Get(source));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment