Created
June 22, 2021 17:08
-
-
Save fdrobidoux/202df0692322f9b79f3def23e79e04c3 to your computer and use it in GitHub Desktop.
ValueRestorer - Restores a value from within a IEnumerator-based Coroutine.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Ada2.Scripting | |
{ | |
public static class Usage | |
{ | |
public static IEnumerator TestEnumeration() | |
{ | |
var component = service.Entity.GetComponent<ValueComponent>(); | |
// When enumerator destroyed, will be disposed, which will restore to the original value. | |
using var valueRestorer = new ValueRestorer() | |
.Assign(x => x.IntValue, component, 25) // Will assign that value instantly. | |
.Assign(x => x.FloatValue, component); // Will not assign any value. | |
// ... Do yields and coroutine logic ... | |
/* Once reachign here or a yield break, values are restored. */ | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics.CodeAnalysis; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
namespace Ada2.Scripting | |
{ | |
public class ValueRestorer : IDisposable | |
{ | |
private List<ValueRestorationData> ValueRestorations = new List<ValueRestorationData>(); | |
public ValueRestorer Assign<T, T2>(Expression<Func<T, T2>> propertExpression, T instance, T2 value) | |
{ | |
ValueRestorations.Add(new ValueRestorationData<T, T2>(propertExpression, instance, value)); | |
return this; | |
} | |
public ValueRestorer Assign<T, T2>(Expression<Func<T, T2>> propertExpression, T instance) | |
{ | |
ValueRestorations.Add(new ValueRestorationData<T, T2>(propertExpression, instance)); | |
return this; | |
} | |
public void Dispose() | |
{ | |
foreach (var value in ValueRestorations) | |
value.Restore(); | |
ValueRestorations.Clear(); | |
} | |
private class ValueRestorationData<T, T2> : ValueRestorationData | |
{ | |
protected T2 OriginalValue { get; set; } | |
protected Expression<Func<T, T2>> PropertyExpr { get; set; } | |
public ValueRestorationData(Expression<Func<T, T2>> propertyExpr, [NotNull]T instance) : base(instance!) | |
{ | |
PropertyExpr = propertyExpr; | |
if (PropertyExpr.Body is MemberExpression) | |
{ | |
OriginalValue = PropertyExpr.Compile().Invoke(instance); | |
} | |
else | |
{ | |
throw new NotSupportedException("Can't use anything but a MemberExpression so far."); | |
} | |
} | |
public ValueRestorationData(Expression<Func<T, T2>> propertyExpr, T instance, T2 NewValue) | |
: this(propertyExpr, instance) | |
{ | |
if (PropertyExpr.Body is MemberExpression memberExpression) | |
{ | |
SetDeepValue(instance, PropertyExpr, NewValue); | |
} | |
else | |
{ | |
throw new NotSupportedException("Can't use anything but a MemberExpression so far."); | |
} | |
} | |
public override void Restore() | |
{ | |
SetDeepValue((T)Instance, PropertyExpr, OriginalValue); | |
} | |
/// All credits for this method goes to xanatos. | |
/// Source on StackOverflow : https://stackoverflow.com/a/29092675/4169987 | |
private static void SetDeepValue(T target, Expression<Func<T, T2>> propertyToSet, T2 valueToSet) | |
{ | |
List<MemberInfo> members = new List<MemberInfo>(); | |
Expression exp = propertyToSet.Body; | |
// There is a chain of getters in propertyToSet, with at the | |
// beginning a ParameterExpression. We put the MemberInfo of | |
// these getters in members. We don't really need the | |
// ParameterExpression | |
while (exp != null) | |
{ | |
MemberExpression mi = exp as MemberExpression; | |
if (mi != null) | |
{ | |
members.Add(mi.Member); | |
exp = mi.Expression; | |
} | |
else | |
{ | |
ParameterExpression pe = exp as ParameterExpression; | |
if (pe == null) | |
{ | |
// We support only a ParameterExpression at the base | |
throw new NotSupportedException(); | |
} | |
break; | |
} | |
} | |
if (members.Count == 0) | |
{ | |
// We need at least a getter | |
throw new NotSupportedException(); | |
} | |
// Now we must walk the getters (excluding the last). | |
object targetObject = target; | |
// We have to walk the getters from last (most inner) to second | |
// (the first one is the one we have to use as a setter) | |
for (int i = members.Count - 1; i >= 1; i--) | |
{ | |
PropertyInfo pi = members[i] as PropertyInfo; | |
if (pi != null) | |
{ | |
targetObject = pi.GetValue(targetObject); | |
} | |
else | |
{ | |
FieldInfo fi = (FieldInfo)members[i]; | |
targetObject = fi.GetValue(targetObject); | |
} | |
} | |
// The first one is the getter we treat as a setter | |
{ | |
PropertyInfo pi = members[0] as PropertyInfo; | |
if (pi != null) | |
{ | |
pi.SetValue(targetObject, valueToSet); | |
} | |
else | |
{ | |
FieldInfo fi = (FieldInfo)members[0]; | |
fi.SetValue(targetObject, valueToSet); | |
} | |
} | |
} | |
} | |
private abstract class ValueRestorationData | |
{ | |
public object Instance { get; } | |
public bool isStruct; | |
public bool isProperty; | |
protected ValueRestorationData([NotNull] object instance) | |
{ | |
Instance = instance; | |
} | |
public abstract void Restore(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment