I've created a class with properties that have default values. At some point in the object's lifetime, I'd like to "reset" the object's properties back to what they were when the object was instantiated. For example, let's say this was the class:
public class Truck {
public string Name = "Super Truck";
public int Tires = 4;
public Truck() { }
public void ResetTruck() {
// Do something here to "reset" the object
}
}
Then at some point, after the Name and Tires properties have been changed, the ResetTruck() method could be called and the properties would be reset back to "Super Truck" and 4, respectively.
What's the best way to reset the properties back to their initial hard-coded defaults?
You can have the initialization in a method instead of inlining with the declaration. Then have the constructor and reset method call the initialization method:
public class Truck {
public string Name;
public int Tires;
public Truck() {
Init();
}
public void ResetTruck() {
Init();
}
private void Init() {
Name = "Super Truck";
Tires = 4;
}
}
Another way is not to have a reset method at all. Just create a new instance.
Reflection is your friend. You could create a helper method to use Activator.CreateInstance() to set the default value of Value types and 'null' for reference types, but why bother when setting null on a PropertyInfo's SetValue will do the same.
Type type = this.GetType();
PropertyInfo[] properties = type.GetProperties();
for (int i = 0; i < properties.Length; ++i)
properties[i].SetValue(this, null); //trick that actually defaults value types too.
To extend this for your purpose, have private members:
//key - property name, value - what you want to assign
Dictionary<string, object> _propertyValues= new Dictionary<string, object>();
List<string> _ignorePropertiesToReset = new List<string>(){"foo", "bar"};
Set the values in your constructor:
public Truck() {
PropertyInfo[] properties = type.GetProperties();
//exclude properties you don't want to reset, put the rest in the dictionary
for (int i = 0; i < properties.Length; ++i){
if (!_ignorePropertiesToReset.Contains(properties[i].Name))
_propertyValues.Add(properties[i].Name, properties[i].GetValue(this));
}
}
Reset them later:
public void Reset() {
PropertyInfo[] properties = type.GetProperties();
for (int i = 0; i < properties.Length; ++i){
//if dictionary has property name, use it to set the property
properties[i].SetValue(this, _propertyValues.ContainsKey(properties[i].Name) ? _propertyValues[properties[i].Name] : null);
}
}
Unless creating the object is really expensive (and Reset isn't for some reason). I see no reason to implement a special reset method. Why don't you just create a new instance with a usable default state.
What is the purpose of reusing the instance?
If you did your initialization in a Reset method you can be good to go:
public class Truck {
public string Name;
public int Tires;
public Truck() {
ResetTruck();
}
public void ResetTruck() {
Name = "Super Truck";
Tires = 4;
}
}
Focusing of separation of concerns (like Brian mentioned in the comments), another alternative would be to add a TruckProperties type (you could even add your default values to its constructor):
public class TruckProperties
{
public string Name
{
get;
set;
}
public int Tires
{
get;
set;
}
public TruckProperties()
{
this.Name = "Super Truck";
this.Tires = 4;
}
public TruckProperties(string name, int tires)
{
this.Name = name;
this.Tires = tires;
}
}
Inside your Truck class, all you would do is manage an instance of the TruckProperties type, and let it do its reset.
public class Truck
{
private TruckProperties properties = new TruckProperties();
public Truck()
{
}
public string Name
{
get
{
return this.properties.Name;
}
set
{
this.properties.Name = value;
}
}
public int Tires
{
get
{
return this.properties.Tires;
}
set
{
this.properties.Tires = value;
}
}
public void ResetTruck()
{
this.properties = new TruckProperties();
}
}
This certainly may be a lot of (unwanted) overhead for such a simple class, but in a bigger/more complex project it could be advantageous.
That's the thing about "best" practices... a lot of times, there's no silver bullet, but only recommendations you must take with skepticism and your best judgement as to what applies to you in a particular case.
I solved a similar problem with reflection. You can use source.GetType().GetProperties() to get a list of all properties which belong to the object.
Although, this is not always a complete solution. If your object implements several interfaces, you will also get all those properties with your reflection call.
So I wrote this simple function which gives us more control of which properties we are interested in resetting.
public static void ClearProperties(object source, List<Type> InterfaceList = null, Type SearchType = null)
{
// Set Interfaces[] array size accordingly. (Will be size of our passed InterfaceList, or 1 if InterfaceList is not passed.)
Type[] Interfaces = new Type[InterfaceList == null ? 1 : InterfaceList.Count];
// If our InterfaceList was not set, get all public properties.
if (InterfaceList == null)
Interfaces[0] = source.GetType();
else // Otherwise, get only the public properties from our passed InterfaceList
for (int i = 0; i < InterfaceList.Count; i++)
Interfaces[i] = source.GetType().GetInterface(InterfaceList[i].Name);
IEnumerable<PropertyInfo> propertyList = Enumerable.Empty<PropertyInfo>();
foreach (Type face in Interfaces)
{
if (face != null)
{
// If our SearchType is null, just get all properties that are not already empty
if (SearchType == null)
propertyList = face.GetProperties().Where(prop => prop != null);
else // Otherwise, get all properties that match our SearchType
propertyList = face.GetProperties().Where(prop => prop.PropertyType == SearchType);
// Reset each property
foreach (var property in propertyList)
{
if (property.CanRead && property.CanWrite)
property.SetValue(source, null, new object[] { });
}
}
else
{
// Throw an error or a warning, depends how strict you want to be I guess.
Debug.Log("Warning: Passed interface does not belong to object.");
//throw new Exception("Warning: Passed interface does not belong to object.");
}
}
}
And it's use:
// Clears all properties in object
ClearProperties(Obj);
// Clears all properties in object from MyInterface1 & MyInterface2
ClearProperties(Obj, new List<Type>(){ typeof(MyInterface1), typeof(MyInterface2)});
// Clears all integer properties in object from MyInterface1 & MyInterface2
ClearProperties(Obj, new List<Type>(){ typeof(MyInterface1), typeof(MyInterface2)}, typeof(int));
// Clears all integer properties in object
ClearProperties(Obj,null,typeof(int));
You'd probably need to save the values off in private fields, so that they can be restored later. Maybe something like this:
public class Truck
{
private static const string defaultName = "Super Truck";
private static const int defaultTires = 4;
// Use properties for public members (not public fields)
public string Name { get; set; }
public int Tires { get; set; }
public Truck()
{
Name = defaultName;
Tires = defaultTires;
}
public void ResetTruck()
{
Name = defaultName;
Tires = defaultTires;
}
}
You're essentially looking for the State Design Pattern
If you want a specific past "state" of your object you can create a particular save point to return every time you want. This also let you have a diferent state to backup for everey instance that you create. If you class has many properties who are in constant change, this could be your solution.
public class Truck
{
private string _Name = "Super truck";
private int _Tires = 4;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
public int Tires
{
get { return _Tires; }
set { _Tires = value; }
}
private Truck SavePoint;
public static Truck CreateWithSavePoint(string Name, int Tires)
{
Truck obj = new Truck();
obj.Name = Name;
obj.Tires = Tires;
obj.Save();
return obj;
}
public Truck() { }
public void Save()
{
SavePoint = (Truck)this.MemberwiseClone();
}
public void ResetTruck()
{
Type type = this.GetType();
PropertyInfo[] properties = type.GetProperties();
for (int i = 0; i < properties.Count(); ++i)
properties[i].SetValue(this, properties[i].GetValue(SavePoint));
}
}
If you aren't using a Code Generator or a Designer that would conflict, another option is to go through C#'s TypeDescriptor stuff, which is similar to Reflection, but meant to add more meta information to a class than Reflection could.
using System.ComponentModel;
public class Truck {
// You can use the DefaultValue Attribute for simple primitive properites
[DefaultValue("Super Truck")]
public string Name { get; set; } = "Super Truck";
// You can use a Reset[PropertyName]() method for more complex properties
public int Tires { get; set; } = 4;
public void ResetTires() => Tires = 4;
public Truck() { }
public void ResetTruck() {
// Iterates through each property and tries to reset it
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(GetType())) {
if (prop.CanResetValue(this)) prop.ResetValue(this);
}
}
}
Note that ResetValue will also reset to a shadowed property if one exists. The priority of which option is selected is explained in the docs:
This method determines the value to reset the property to in the following order of precedence:
There is a shadowed property for this property.
There is a DefaultValueAttribute for this property.
There is a "ResetMyProperty" method that you have implemented, where "MyProperty" is the name of the property you pass to it.
You may represent an object state as a struct or record struct and then set the state to the default value in the Reset method like this:
public class Truck {
record struct State(string Name, int Tires);
private static readonly State _defaultState = new("Super Truck", 4);
private State _state = _defaultState;
public string Name => _state.Name;
public int Tires => _state.Tires;
public Truck() {}
public void ResetTruck() => _state = _defaultState;
}
It is probably the fastest way as well.
Also, a record struct will give you the trivial implementations of the ToString, Equals, GetHashCode.
Related
I want to index class properties like an array.
Public class Foo
{
propery p1{get;set;}
propery p3{get;set;}
propery p3{get;set;}
.
.
.
.
}
I wan to index every propery like an array
FOO.p1=Value
Foo[0]=Value(index 0 refers to p1)
I don't know much about the database, where there might have been a ready-made solution. But at least you can do it by reflection in this way:
using System.Reflection;
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class IndexedPropertyAttribute : Attribute
{
readonly int index;
public IndexedPropertyAttribute(int index)
{
this.index = index;
}
public int Index
{
get { return index; }
}
}
public abstract class WithIndexedProperties
{
private Lazy<IReadOnlyDictionary<int, PropertyInfo>> properties;
protected WithIndexedProperties()
{
properties = new Lazy<IReadOnlyDictionary<int, PropertyInfo>>(
() => {
var linq = from prop in this.GetType().GetProperties()
let attr = prop.GetCustomAttributes(typeof(IndexedPropertyAttribute), true)
where attr.Length is 1
select (((IndexedPropertyAttribute)attr[0]).Index, prop);
return linq.ToDictionary(p => p.Index, p => p.prop);
});
}
public object? this[int propertyIndex]
{
get
{
return properties.Value[propertyIndex].GetValue(this);
}
set
{
properties.Value[propertyIndex].SetValue(this, value);
}
}
}
And there is an example:
Clss obj = new Clss();
obj[0] = "ABC";
obj[2] = 222;
obj[4] = 444;
// Here obj.A will be "ABC", obj.B will be 444 and obj.C will be 222.
public class Clss : WithIndexedProperties
{
[IndexedProperty(0)]
public string? A { get; init; }
[IndexedProperty(4)]
public int B { get; init; }
[IndexedProperty(2)]
public int C { get; init; }
}
I think you need to do something like this. The code below is very generalized solution to your question and I might need some customization for yourself
using System.Reflection;
public class ReflectionBasedIndexedType
{
public int A1 { get; set; } = 10;
public int A2 { get; set; } = 20;
public string SomeString => "Hello There";
private readonly Dictionary<string, object> _underlyingCollection;
public object this[string name] => _underlyingCollection[name];
public ReflectionBasedIndexedType()
{
_underlyingCollection = GetUnderlyingCollection();
}
private Dictionary<string, object> GetUnderlyingCollection()
{
Dictionary<string, object> container = new();
// get the properties defined in the class, I am filtering
// with constraint that, I want get only public and class level
// Properties, which means I won't get any private/protected or
// static properties if there is defined such in the class
// also where filters out indexer property, which we have defined
// inside this class, without this line, there will be exception
IEnumerable<PropertyInfo> properties = GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.GetIndexParameters().Length == 0);
foreach (PropertyInfo property in properties)
{
container.Add(property.Name, property.GetValue(this)!);
}
return container;
}
}
and than use case will be like this
ReflectionBasedIndexedType rbit = new();
var a1 = rbit[nameof(rbit.A1)];
var a2 = rbit[nameof(rbit.A2)];
var str = rbit[nameof(rbit.SomeString)];
Console.WriteLine(a1);
Console.WriteLine(a2);
Console.WriteLine(str);
and output in the console will be this
10
20
Hello There
I think you have two ways at least.
The first one, is #Swapper mentioned, yo can use reflection. In this way, the class is normal and you have to write your hug code in where you want to use that class.
The second way is easier but a little fuzy. You can use dynamic type. If you know how to use it, that's ok. Otherwise please let me know, then I will create a sample code for you.
What I am trying to do: I am trying to make component based Objects which can be easly created with custom set type of rules for each component value.
How I am doing it: I have created IComponent interface which each component implements. All components need to be structs, example:
public struct Weight : IComponent
{
int weight;
}
Each Object is defined by just list of components with their values. Then to make it custom set of rules I made ObjectSettings which holds list of generic class ComponentSetup<T> where T : IComponent. ComponentSetup is a class which by reflection gets list of fields in IComponent and pairs them in Dicionary as FieldName and GenerationType for field. For example: for Object "Car" :
Car:
Weight:
weight:
GenerationType: RandomRange
Min: 1200
Max: 1700
For Object "Human":
Human:
Weight:
weight:
GenerationType: NormalDistribution
Expected Value: 70
Variance: 4
For Object "1kg dumbbell":
1kgDumbbell:
Weight:
weight:
GenerationType: Fixed
Value: 1
In order to get generated Objects I used reflection to set values of components compose in List and return as Object.
The problem with this approach: When I want to generate 5k-10k of those Objects it takes way too much time.
My solution so far: I generate semi filled objects(on startup) and store them as prefabs in PrefabManager. They are Objects with components' values set only if their GenerationType is "Fixed" and then only fill values with other types of Generation.
My question: How can I make setting values by reflection faster, if it's not possible then how can I get the same result but faster? I also would like to keep prefab generation on startup because they help me instantiating Objects because I don't need to create whole new object, just copy prefab and fill it, which is faster in my case.
EDIT: Adding example code. I didn't test it however it should be easy to understand what I am trying to do:
namespace Example
{
//ProceduralObject Component intreface
public interface IComponent
{
}
//Example component for procedural object
public struct Weight : IComponent
{
public int weight;
}
//object with procedurally generated components
public class ProceduralObject
{
public List<IComponent> components = new List<IComponent>();
}
public class ProceduralObjectSettings
{
public Dictionary<string,ComponentSetup> ComponentSetups = new Dictionary<string,ComponentSetup>();
public ProceduralObjectSettings()
{
}
public void AddComponent(Type t)
{
//check if added component is assignable from correct interface
if (t.IsAssignableFrom(typeof(IComponent))) ComponentSetups.Add(t.Name,new ComponentSetup(t));
}
//getting ProceduralObject with generated components
public ProceduralObject getGeneratedObject()
{
ProceduralObject newObject = new ProceduralObject();
foreach (var componentSetup in ComponentSetups)
{
newObject.components.Add(componentSetup.Value.getGeneratedComponent());
}
return newObject;
}
}
public class ComponentSetup
{
// Collection of properties of IComponent it represents
public Dictionary<string, IGenerationType> propertyGenerationSettings = new Dictionary<string, IGenerationType>();
// Type of IComponent it represents
public Type t;
public ComponentSetup(Type t)
{
this.t = t;
//Getting all fields of represented IComponent and adding them to propertyGenerationSettings with default GenerationType
var fields = t.GetFields();
for (int i = 0; i < fields.Length; i++)
{
propertyGenerationSettings.Add(fields[i].Name,new EmptyGenerationType());
}
}
//Generating new component with settings
public IComponent getGeneratedComponent()
{
IComponent toReturn = (IComponent)Activator.CreateInstance(t);
var fields = toReturn.GetType().GetFields();
foreach (var property in propertyGenerationSettings)
{
var fieldInfo = fields.First(field => field.Name == property.Key);
toReturn.GetType().SetMemberValue(fieldInfo, property.Value.GetGeneratedValue());
}
return toReturn;
}
}
public interface IGenerationType
{
System.Object GetGeneratedValue();
}
public class EmptyGenerationType : IGenerationType
{
public object GetGeneratedValue()
{
throw new Exception("You can't use EmptyGenerationType");
}
}
public class RandomRangeGenerationType : IGenerationType
{
private double min, max;
public RandomRangeGenerationType(double min, double max)
{
this.min = min;
this.max = max;
}
public object GetGeneratedValue()
{
return null; /* return */
}
}
public class NormalDistributionGenerationType : IGenerationType
{
private double expectedValue, variance;
public NormalDistributionGenerationType(double expectedValue, double variance)
{
this.expectedValue = expectedValue;
this.variance = variance;
}
public object GetGeneratedValue()
{
return null; /* return */
}
}
public class FixedGenerationType : IGenerationType
{
public double value;
public FixedGenerationType(double value)
{
this.value = value;
}
public object GetGeneratedValue()
{
return null;
}
}
public class Example
{
public void Main()
{
Dictionary<string,ProceduralObjectSettings> proceduralObjectsCollection = new Dictionary<string,ProceduralObjectSettings>();
proceduralObjectsCollection.Add("Car",new ProceduralObjectSettings());
proceduralObjectsCollection["Car"].AddComponent(typeof(Weight));
proceduralObjectsCollection["Car"].ComponentSetups["Weight"].propertyGenerationSettings["weight"] = new RandomRangeGenerationType(1200,1700);
proceduralObjectsCollection.Add("Human",new ProceduralObjectSettings());
proceduralObjectsCollection["Human"].AddComponent(typeof(Weight));
proceduralObjectsCollection["Human"].ComponentSetups["Weight"].propertyGenerationSettings["weight"] = new NormalDistributionGenerationType(70,4);
proceduralObjectsCollection.Add("1kgDumbbell",new ProceduralObjectSettings());
proceduralObjectsCollection["1kgDumbbell"].AddComponent(typeof(Weight));
proceduralObjectsCollection["1kgDumbbell"].ComponentSetups["Weight"].propertyGenerationSettings["weight"] = new FixedGenerationType(1);
}
}
}
Reflection is slow, but the execution of delegates is fast. Therefore, if you need to execute things obtained by reflection very often, it is good practise to use reflection to create a delegate and that use that delegate.
Creating the delegate is very simple, as long as you know the type of the property and the type that declares the property. The easiest way to get them is to have these types as generic type parameters in an open generic type. Then, you can close the type at runtime (using MakeGenericType on System.Type) and instantiate the closed generic type using System.Activator.CreateInstance. This is of course costly, but you only need to create objects that describe the properties of your models once and then use it as factories for as many instances as you like without any reflection calls.
Edit: this is how it can look like using properties instead of fields, based on your example code
If you really want to go with fields (which I would encourage you not to do), creating the delegate is slightly more complex (use the expression compiler or emit IL code), but the principal approach remains the same.
public class ComponentSetup
{
// Collection of properties of IComponent it represents
private Dictionary<string, PropertySetter> propertyGenerationSettings = new Dictionary<string, PropertySetter>();
// Type of IComponent it represents
public Type t;
public ComponentSetup( Type t )
{
this.t = t;
//Getting all fields of represented IComponent and adding them to propertyGenerationSettings with default GenerationType
var fields = t.GetProperties();
for(int i = 0; i < fields.Length; i++)
{
var propertySetterType = typeof( PropertySetter<,> ).MakeGenericType( t, fields[i].PropertyType );
var setter = (PropertySetter)Activator.CreateInstance( propertySetterType, fields[i] );
propertyGenerationSettings.Add( fields[i].Name, setter );
}
}
public void SetGenerator<T>( string property, IGenerationType<T> generator )
{
propertyGenerationSettings[property].SetGenerator( generator );
}
//Generating new component with settings
public IComponent getGeneratedComponent()
{
IComponent toReturn = (IComponent)Activator.CreateInstance( t );
foreach(var property in propertyGenerationSettings)
{
property.Value.Set( toReturn );
}
return toReturn;
}
}
internal abstract class PropertySetter
{
public abstract void Set( object target );
public abstract void SetGenerator( object generator );
}
internal class PropertySetter<T, TField> : PropertySetter
{
private Action<T, TField> setter;
private IGenerationType<TField> generator;
public PropertySetter( PropertyInfo property )
{
setter = (Action<T, TField>)property.SetMethod.CreateDelegate( typeof( Action<T, TField> ) );
generator = new EmptyGenerationType<TField>();
}
public override void Set( object target )
{
if(target is T targetObj)
{
setter( targetObj, generator.GetGeneratedValue() );
}
}
public override void SetGenerator( object generator )
{
this.generator = (generator as IGenerationType<TField>) ?? this.generator;
}
}
public interface IGenerationType<T>
{
T GetGeneratedValue();
}
Setup:
public class Data
{
public int A { get; set; }
public int B { get; set; }
}
public class Runner
{
public static void Run(Data data)
{
data.A = data.B;
data.A = 1;
}
}
class Program
{
static void Main(string[] args)
{
var data = new Data() { A = 1, B = 2 };
Runner.Run(data);
}
}
Problem: I need to implement change tracking here for property names not values. Inside Runner.Run on the first line data.A = data.B I need to record somehow that "A" was set to "B" (literally property names) and then on the next line data.A = 1 I need to record that "A" was set to constant and say forget about it.
Constrains:
When setting one property to another (e.g. A = B) that needs to be recorded
When setting property to anything else (e.g. A = 1 or A = B * 2) this change needs to be forgotten (e.g. remember A only)
Suppose this is the tracker contract being used:
void RecordChange(string setterName, string getterName);
void UnTrackChange(string setterName);
Question:
I would like to somehow proxy the Data class so it still can be used in the interface code (e.g. Runner - is a whole bunch of a business logic code that uses Data) INCLUDING strong-typing and it can track it's changes without modifying the code (e.g. there is lots of places like 'data.A = data.B').
Is there any way to do it without resorting to I guess some magic involving IL generation?
Already investigated/tried:
PostSharp interceptors/Castle.DynamicProxy with interceptors - these alone cannot help. The most I can get out of it is to have a value of data.B inside setter interceptor but not nameof(data.B).
Compiler services - haven't found anything suitable here - getting the name of caller doesn't really help.
Runtine code generation - smth like proxy inherited from DynamicObject or using Relfection.Emit (TypeBuilder probably) - I lose typings.
Current solution:
Use the Tracker implementation of the abovementioned contract and pass it around into every function down the road. Then instead of writing data.A = data.B use method tracker.SetFrom(x => x.A, x => x.B) - tracker holds a Data instance and so this works. BUT in a real codebase it is easy to miss something and it just makes it way less readable.
It is the closest the solution I've come up with. It isn't perfect as I still need to modify all the contracts/methods in the client code to use a new data model but at least all the logic stays the same.
So I'm open for other answers.
Here's the renewed Data model:
public readonly struct NamedProperty<TValue>
{
public NamedProperty(string name, TValue value)
{
Name = name;
Value = value;
}
public string Name { get; }
public TValue Value { get; }
public static implicit operator TValue (NamedProperty<TValue> obj)
=> obj.Value;
public static implicit operator NamedProperty<TValue>(TValue value)
=> new NamedProperty<TValue>(null, value);
}
public interface ISelfTracker<T>
where T : class, ISelfTracker<T>
{
Tracker<T> Tracker { get; set; }
}
public class NamedData : ISelfTracker<NamedData>
{
public virtual NamedProperty<int> A { get; set; }
public virtual NamedProperty<int> B { get; set; }
public Tracker<NamedData> Tracker { get; set; }
}
Basically I've copy-pasted the original Data model but changed all its properties to be aware of their names.
Then the tracker itself:
public class Tracker<T>
where T : class, ISelfTracker<T>
{
public T Instance { get; }
public T Proxy { get; }
public Tracker(T instance)
{
Instance = instance;
Proxy = new ProxyGenerator().CreateClassProxyWithTarget<T>(Instance, new TrackingNamedProxyInterceptor<T>(this));
Proxy.Tracker = this;
}
public void RecordChange(string setterName, string getterName)
{
}
public void UnTrackChange(string setterName)
{
}
}
The interceptor for Castle.DynamicProxy:
public class TrackingNamedProxyInterceptor<T> : IInterceptor
where T : class, ISelfTracker<T>
{
private const string SetterPrefix = "set_";
private const string GetterPrefix = "get_";
private readonly Tracker<T> _tracker;
public TrackingNamedProxyInterceptor(Tracker<T> proxy)
{
_tracker = proxy;
}
public void Intercept(IInvocation invocation)
{
if (IsSetMethod(invocation.Method))
{
string propertyName = GetPropertyName(invocation.Method);
dynamic value = invocation.Arguments[0];
var propertyType = value.GetType();
if (IsOfGenericType(propertyType, typeof(NamedProperty<>)))
{
if (value.Name == null)
{
_tracker.UnTrackChange(propertyName);
}
else
{
_tracker.RecordChange(propertyName, value.Name);
}
var args = new[] { propertyName, value.Value };
invocation.Arguments[0] = Activator.CreateInstance(propertyType, args);
}
}
invocation.Proceed();
}
private string GetPropertyName(MethodInfo method)
=> method.Name.Replace(SetterPrefix, string.Empty).Replace(GetterPrefix, string.Empty);
private bool IsSetMethod(MethodInfo method)
=> method.IsSpecialName && method.Name.StartsWith(SetterPrefix);
private bool IsOfGenericType(Type type, Type openGenericType)
=> type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType;
}
And the modified entry point:
static void Main(string[] args)
{
var data = new Data() { A = 1, B = 2 };
NamedData namedData = Map(data);
var proxy = new Tracker<NamedData>(namedData).Proxy;
Runner.Run(proxy);
Console.ReadLine();
}
The Map() function actually maps Data to NamedData filling in property names.
I have a few classes which have some primitive fields and I would like to create a generalized wrapper for them in order to access their fields. This wrapper should somehow contain a reference to the fields of my classes so that I can read/write the values of these fields. The idea is to create a genralized architecture for these classes so that I dont have to write code for each of them. The classes have fields which have a number in them which will be used as an Id to access the fields.
This is some example code that might shed some light on my requirement. What I want in the end is to change the value of some field in the object of Fancy1 class without accessing the object itself but through its wrapper.
class Fancy1
{
public double level1;
public bool isEnable1;
public double level2;
public bool isEnable2;
public double level3;
}
class Fancy2
{
public double level4;
public bool isEnable4;
public double level6;
public bool isEnable6;
public double level7;
}
class FieldWrapper
{
public int id { get; set; }
public object level { get; set; }
public object isEnabled { get; set; }
public FieldWrapper(int id, object level, object isEnabled)
{
this.id = id;
this.level = level;
this.isEnabled = isEnabled;
}
}
class FancyWrapper
{
private Fancy scn;
public FancyWrapper(Fancy scn)
{
if (!(scn is Fancy))
throw new ArgumentException(scn.GetType().FullName + " is not a supported type!");
this.scn = scn;
}
private Dictionary<int, FieldWrapper> fieldLut = new Dictionary<int, FieldWrapper>();
private List<FieldWrapper> _fields { get { return fieldLut.Values.ToList(); } }
public List<FieldWrapper> fields
{
get
{
if (_fields.Count == 0)
{
foreach (System.Reflection.FieldInfo fieldInfo in scn.GetType().GetFields())
{
if (fieldInfo.FieldType == typeof(double))
{
int satId = getIdNr(fieldInfo.Name);
fieldLut.Add(satId, new FieldWrapper(satId, fieldInfo.GetValue(scn), true));
}
}
foreach (System.Reflection.FieldInfo fieldInfo in scn.GetType().GetFields())
{
if (fieldInfo.FieldType == typeof(bool))
{
int satId = getIdNr(fieldInfo.Name);
fieldLut[satId].isEnabled = fieldInfo.GetValue(scn);
}
}
}
return _fields;
}
}
private int getIdNr(string name)
{
System.Text.RegularExpressions.Match m = System.Text.RegularExpressions.Regex.Match(name, #"\d+");
return Int32.Parse(m.Value);
}
}
class Program
{
static void Main(string[] args)
{
Fancy1 fancy = new Fancy1();
fancy.level1 = 1;
fancy.isEnable1 = true;
fancy.level2 = 2;
fancy.isEnable2 = false;
fancy.level3 = 3;
FancyWrapper wrapper = new FancyWrapper(fancy);
wrapper.fields[2].level = 10;
// fancy.level2 should somehow get the value I set via the wrapper
Console.WriteLine(fancy.level2);
Console.ReadLine();
}
}
EDIT: Fancy classes cannot be changed since they are part of an interface!
Depending on how many Fancy classes you are dealing with, you could create an adapter/facade class for each the expose a common interface. eg:
class Fancy1
{
public double level1;
public bool isEnable1;
public double level2;
public bool isEnable2;
public double level3;
}
public class FieldWrapper
{
private Action<double> _levelSetter;
private Func<double> _levelGetter;
private Action<bool> _enableSetter;
private Func<bool> _enableGetter;
public double level { get { return _levelGetter(); } set { _levelSetter(value); }}
public bool isEnabled { get { return _enableGetter(); } set { _enableSetter(value); }}
internal FieldWrapper(Func<double> levelGetter, Action<double> levelSetter, Func<bool> enableGetter, Action<bool> enableSetter)
{
_levelGetter = levelGetter;
_levelSetter = levelSetter;
_enableGetter = enableGetter;
_enableSetter = enableSetter;
}
}
abstract class FancyWrapper
{
public FieldWrapper[] Fields { get; protected set; }
}
class Fancy1Wrapper : FancyWrapper
{
private Fancy1 _fancy1;
public Fancy1Wrapper(Fancy1 fancy1)
{
_fancy1 = fancy1;
this.Fields = new[] { new FieldWrapper(() => fancy1.level1, level => _fancy1.level1 = level, () => _fancy1.isEnable1, enable => _fancy1.isEnable1 = enable),
new FieldWrapper(() => fancy1.level2, level => _fancy1.level2 = level, () => _fancy1.isEnable2, enable => _fancy1.isEnable2 = enable), };
}
}
Or you could invest 5 minutes learning data structures. Consider following example:
var levels = new Dictionary<int, bool>
{
{1, true},
{2, false}
};
if (levels[1])
{
//will run, because level 1 is true
}
if (levels[2])
{
//will not run, because level 2 is false
}
if (levels.ContainsKey(3) && levels[3])
{
//will not run, because dictionary does not contain entry for key 3
}
levels.Add(3, false);
if (levels.ContainsKey(3) && levels[3])
{
//will not run, because level 3 is false
}
levels[3] = true;
if (levels.ContainsKey(3) && levels[3])
{
//will run, because level 3 is true
}
That may seem like what you want, but it really isn't. It is extremely awkward on any number of levels. More specifically, pointers are generally rather "Un-C#-like" and having to know about these numbers defeats the point of having separate classes to begin with.
Think closely about what you want to accomplish. If you're having problems translating it into code, we're here to help. :)
I am trying to implement a string indexer for a C# class, but when you set a property the dictionary gets set and not the property. It is probably something simple that i am missing, i just can't see it.
objFiveProp temp = new objFiveProp();
temp["index1"] = 3;
sets the temp._items["index1"].value to 3.
Class:
public class objFiveProp
{
#region Properties
private Dictionary<string, int> _items;
public int this[string key]
{
get { return _items[key]; }
set { _items[key] = value; }
}
public int index1 { get; set; }
public int index2 { get; set; }
public int index3 { get; set; }
public int index4 { get; set; }
public int index5 { get; set; }
#endregion
#region Constructor
public objFiveProp()
{
index1 = 0;
index2 = 0;
index3 = 0;
index4 = 0;
index5 = 0;
_items = new Dictionary<string, int>();
_items.Add("index1", index1);
_items.Add("index2", index2);
_items.Add("index3", index3);
_items.Add("index4", index4);
_items.Add("index5", index5);
}
#endregion
}
That's how it works. The Dictionary contains a copy of the integers you use to set it up - not a reference to the properties.
I would tackle this by using something like:
public class objFiveProp
{
private Dictionary<string, int> _items;
public int this[string key]
{
get { return _items[key]; }
set { _items[key] = value; }
}
public int Index1
{
get { return this["index1"]; }
set { this["index1"] = value; }
}
public int Index2
{
get { return this["index2"]; }
set { this["index2"] = value; }
}
// ....
public objFiveProp()
{
_items = new Dictionary<string, int>();
_items.Add("index1", index1);
_items.Add("index2", index2);
_items.Add("index3", index3);
_items.Add("index4", index4);
_items.Add("index5", index5);
}
#endregion
This causes your properties to always pull the values stored in your dictionary, as well as save there, so there aren't two copies of the values.
This is because in index set method you are setting the value of dictionary item
public int this[string key]
{
get { return _items[key]; }
set { _items[key] = value; } //here you are setting the value of dictionary item not the property
}
Either, create separate property for index1, index2 and so on or in set method above add checks, a dirty solution though, to set the value of member variable depending on the value of key; Something like:
set {
_items[key] = value;
if(key == "index1")
index1 = value;
}
int is a value type, not a reference type. When you set a value in the _items, it won't set the property, even though you initially added it from the property.
From MSDN
Variables that are based on value types directly contain values.
Assigning one value type variable to another copies the contained
value. This differs from the assignment of reference type variables,
which copies a reference to the object but not the object itself.
If you really need to be able to access your data both from the indexer and the property, one of the simplest way would be to rewrite your properties thusly:
public int indexN
{
get { return _items["indexN"]; }
set { _items["indexN"] = value; }
}
Another way would be to use reflection in the indexer's setter:
public int this[string key]
{
get { return _items[key]; }
set
{
_items[key] = value;
PropertyInfo prop = this.GetType().GetProperty(key);
if (prop != null)
{
prop.SetValue(this, null);
}
}
}
Remember though, reflection is relatively very S-L-O-W.
There are other ways to accomplish what you're trying to do as well, but perhaps the best solution is to not do this at all. Pick the best interface to your class, whether that be the indexer or the properties, and stick with it. Your code will be more maintainable (you won't have to upkeep two public interfaces to your class's data) and more readable (other coders won't need to know that the indexer and the properties are the same thing). Cheers!