Creating a list composed of object property references - c#

I am trying to do the following thing:
- From within a 1st method, I am going through a bunch of objects (of same type) and extracting pointers to specific properties into a list
- This list will then be fed to a another method elsewhere in my program at some point in time and has to modify all the properties (as per provided list) of the original objects
In other words, say we have the following class:
public class Something
{
public SomeFlag = False;
public Something()
{
}
}
Somewhere in the system, we have composed a related list of objects into List.
Now, we want to scan through this list and extract into "List< bool> flags" all the flags (by reference?):
List<bool> flags = new List<bool>();
foreach (var stuff in List<Something>)
{
flags.Add(stuff.SomeFlag);
}
Finally, somewhere else, I want to update these flags, but the update should affect the original object:
public static void SetStates(List<bool> myList)
{
// The flag should get set to true by reference in the original object
myList.SomeFlag = True;
}

Using actions could be one way to achive this:
public class Something
{
public bool SomeFlag { get; set; }
}
internal class Program
{
private static void Main()
{
var somethings = new[] {new Something(), new Something()};
var flags = new List<Action<bool>>();
// create your list of 'pointers'
foreach (var something in somethings)
{
flags.Add(x => something.SomeFlag = x);
}
// set them all to true
foreach (var action in flags)
{
action(true);
}
// check the results
foreach (var something in somethings)
{
Console.WriteLine(something.SomeFlag);
}
Console.WriteLine("press any key to exit...");
Console.ReadLine();
}
}

In C#, you cannot save a reference to a property value (like a pointer to the memory location where the value is stored). You only can save a reference to an object which contains this property value.
In your var list = new List<Something>(), you can store those references to the objects.
Note that it's impossible for value types though. If Something is a struct, not a class, then the list will contain copies of the objects, not the references to the objects. So the rest of my answer assumes we're talking about class Something.
You can define a property changing behavior and apply it using the list of the objects.
If you already know at compile time which properties and which values do you need, you can create a lambda and pass it around.
// Define the behavior and get the object list
Action<Something> setter = o => o.Someflag = true;
var objectList = new List<Something>();
// Call your processing method later on
SetProperties(objectList, setter);
void SetProperties<T>(List<T> objects, Action<T> setter)
{
objects.ForEach(setter);
}
If you don't know at compile which properties and which values you will need, then things get much more complicated. You will need to use Reflection to obtain the property descriptors and to set the values.
Here is a simplified example:
// Define the behavior and get the object list
var objectList = new List<Something>();
string propertyName = "SomeFlag";
PropertyInfo pi = typeof(Something).GetProperty(propertyName);
MethodInfo setter = pi.GetSetMethod();
object value = true;
// Call your processing method later on
SetProperties(objectList, setter, value);
void SetProperties<T>(List<T> objects, MethodInfo setter, object value)
{
var arguments = new object[] { value } ;
objects.ForEach(o => setter.Invoke(o, arguments));
}

Related

How to cast PropertyInfo to its own type using reflection

I am trying to use reflection for getting the property name declared and its value, I am able to get the declared property name using property info the main concern I am having is I want to get the value for the property and I don't know the object type so I cant cast directly.
I know we need to use item.GetValue(object) but here the object, I need to pass using reflection.
For example, if you see the below code
Class structure
public abstract class ObjectInputs{}
public class ValveInputs : ObjectInputs
{
public Conditions Conditions { get; set; } = new Conditions();
}
public class Conditions :IExportable
{
[CanExportAttribute]
public string north {get;set;}
}
Method
public void Append(Scenario scenario)
{
var scenarioInputs = (commonDomain.ObjectInputs)scenario.Inputs; // ObjectInputs is an abstract class
var exportableInputs = scenarioInputs.GetType().GetProperties().Where(x =\> typeof(IExportable).IsAssignableFrom(x.PropertyType)); // I extraced property having interface IExportable
var listOfExportableProperties = new ScenarioExtract();
foreach (var exportableInput in exportableInputs)
{
var allProperties = ((System.Reflection.TypeInfo)exportableInput.PropertyType).DeclaredProperties; // Got all the property details
var propertyHavingAttribute = allProperties.Where(x =\> x.CustomAttributes.Where(z =\> z.AttributeType == typeof(CanExportAttribute)).Any()).ToArray(); // Got the properties which i need to extract.
The issue is here, if i do this then its creating a new instance and the values of each properties are set to default. I want to cast the exportableInput to its type (I cant hard code the type casting) so that i can use the value below.
object destination = Activator.CreateInstance(scenarioInputs.GetType());
foreach (var item in propertyHavingAttribute)
{
var detail = new InputPropertyDetail { InputName = item.Name, InputValue = \*\*item.GetValue(destination).ToString() \*\*}; \*\*want to use value here\*\*
listOfExportableProperties.PropertyDetails.Add(detail);
}
}
spreadsheetBuilder.AppendComponenet(listOfExportableProperties);
}
If you're using Activator.CreateInstance, it will always create a new instance (as the name inplies) with default values. Instead you must use the value of the exportableInput property.
object destination = exportableInput.GetValue(scenarioInputs);
Then you can get the actual value of the exportable property of the instance with InputValue = item.GetValue(destination).ToString().

Perform a 'task' via an enum and a field, by checking the 'T' type, and inferring it into the task automatically

I've come across an issue that I cannot solve. I've got an IReadOnlyList of classes that each have a bunch of fields. These fields have names (variable names) identical to a list of enums. Think that for each field that exists, an enum for it with the exact same name also exists (so object helloHi has an equivalent enum something { helloHi }).
What I've attempted to do is compare the two field names. If they are identical, perform a function on them. The problem is that the function needs to infer a T from the variable, and since reflection isn't able to pull that 'T' without some form of cast, it won't proceed.
This is the code:
public class Something() {
[BackgroundTask]
private void load(Overlay param_1, Config param_2) {
Children = new Drawable[] // is the IReadOnlyList
{
SomethingClass(param_1),
AnotherClass(param_2)
}
performTask(this, param_2);
}
}
public class Config {
public void task<U>(SomeEnums se, ValueType<U> value) // do the task
}
public class SomethingClass {
ValueType<double> someDouble = new ValueType<double>();
ValueType<int> someInt = new ValueType<int>();
public SomethingClass(Overlay overlay) //...
}
public enum SomeEnums {
someDouble,
someInt,
}
void performTask(Something the_class, Config the_config) {
// ... for each field within the_class, do (uses reflection)
field => {
foreach (SomeEnums enums in Enum.GetValues(typeof(SomeEnums)))
{
if (field.Name == enums.ToString()) {
the_config.task(enums, field.GetValue(null)); // cant infer 'U' from an 'object'
}
}
}
}
Technically, I could just do the config.task within the class where the types are known and visible, but I'd much prefer to automate it from here, so that it doesn't need 2-3 changes every time a new variable is created.
One of the strategies I am aware of is performing an if check within the performTask like such:
// performTask, field =>, foreach
{
if (field.FieldType == ValueType<double>)
config.task(enums, (ValueType<double>)field.GetValue(null));
} //etc
However, I don't like this method. It would just need to introduce more and more checks if I ever created more ValueType<> and if they aren't already being checked for. Would there be a better way to perform what I want?
As I mentioned in my comment, I can't quite tell what you really want to do. However, here's some code that should help you figure it out.
It uses reflection to get the fields of objects, look at the names of those fields (comparing them to the values/names associated with an enum type) and compare the values. I do a comparison to integer 5, but you could compare to anything (but, it appears that the integer type's implementation of IComparable.CompareTo throws if it's compared to something other than an int, so I check). Since you know the type of everything, this is easy to check (you don't have to compare to a fixed Type, you can use what is returned by GetType()).
I started with some auxiliary types:
public enum SomeEnums {
SomeDouble,
SomeInt,
}
public class Class1 {
public int SomeInt = 5;
public double SomeDouble = 3.14;
}
public class Class2 {
public int SomeInt = 5;
public double SomeDouble = 6.28;
}
and then added this:
public class EnumFields {
public List<object> Objects = new List<object> {
new Class1(),
new Class2(),
};
public void PerformTask () {
var enumVals = Enum.GetNames(typeof(SomeEnums));
foreach (var obj in Objects) {
var objType = obj.GetType();
var fieldInfos = objType.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
//right around here I get lost. You have a list of objects (which has two instances right now),
//What are you comparing, that every field named SomeInt has the same value??
//Anyway, here's some code that should help you
foreach (var fieldInfo in fieldInfos) {
if (enumVals.Contains(fieldInfo.Name)) {
var fieldObj = fieldInfo.GetValue(obj);
var isSame = false;
if (fieldObj.GetType() == typeof(int)) {
var comparable = (IComparable)fieldObj;
var same = comparable.CompareTo(5);
isSame = (same == 0);
}
Debug.WriteLine($"Field: {fieldInfo.Name} of instance of {obj.GetType().Name} (Value: {fieldObj}) is equal to 5:{isSame}");
}
}
}
}
}
When I instantiate an EnumFields object and call PerformTask on it, I see this in the output:
Field: SomeInt of instance of Class1 (Value: 5) is equal to 5:True
Field: SomeDouble of instance of Class1 (Value: 3.14) is equal to 5:False
Field: SomeInt of instance of Class2 (Value: 5) is equal to 5:True
Field: SomeDouble of instance of Class2 (Value: 6.28) is equal to 5:False
This should get you most of the way there. I realize it doesn't answer your question. Had I been able to figure out what you were asking, it probably would have.

Why my method changes the original value of the object that is passed to it?

I have the following object
var filters = new List<IReportFilter>
{
new ReportFilter
{
ReportColumn = new ReportColumn{ ColumnKey = "Result.IsCompleted"},
Value = "1",
SubFilters = new List<IReportFilter>
{
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ ColumnKey = "User.LastName"}, Value = "Alhayek"},
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ ColumnKey = "User.LastName"}, Value = "Smith"},
new ReportFilter { SqlOperator = FilterOperator.Or, ReportColumn = new ReportColumn{ AggregateFunction = SqlAggregateFunctions.Count}, Type = FilterType.GreaterThenOrEqualTo ,Value = "0" },
}
},
};
The obove object is passed to another class using a method like so
IReportModel ReportModel = Template.CreateReport();
ReportModel.Get(filters);
Inside the the Get method of the ReportModel class I want to loop through the filters list and create a new list without changing the original list. the new list will become a subset of the original.
From with in my Get method here is what I have done
public SqlCommand Build(List<IReportFilter> filters)
{
var a = CloneFilters(filters);
var b = CloneFilters(filters);
List<IReportFilter> standardFilters = ExtractFiltersByAType(a, true);
List<IReportFilter> aggregateFilter = ExtractFiltersByAType(b, false);
}
But every time I execute the method ExtractFiltersByAType the value of a,b, and filters change to equal the same value of aggregateFilter.
I am NOT expecting for any of the variables to change. But they are for some reason that I don't understand.
Here is my CloneFilters method
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter);
}
return copyOfFilters;
}
And here is my ExtractFiltersByAType
private List<IReportFilter> ExtractFiltersByAType(List<IReportFilter> filtersSource, bool IsStandard = true)
{
List<IReportFilter> validFilters = new List<IReportFilter>();
foreach (var filterSource in filtersSource)
{
if (filterSource.SubFilters != null && filterSource.SubFilters.Any())
{
filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard); //I think this what could be causing this problem
}
if ((IsStandard && !filterSource.ReportColumn.IsAggregate) || (!IsStandard && filterSource.ReportColumn.IsAggregate))
{
validFilters.Add(filterSource);
}
}
return validFilters;
}
Question
Since I am not using ref to pass the object by reference to the method, why is my function changing the value to original object?
When passing a list of object to method in c#, will the system create a copy or will it passes the object by reference?
How can I solve this problem so that every time I execute ExtractFiltersByAType method, only the copy is changed not the originals?
I am thinking that the line filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard); in the ExtractFiltersByAType is causing the problem but I don't understand why and how.
Without ref
When you pass a reference type as an argument (which includes a list), you pass a copy of the reference to that object. This means you can change your object attributes, but can't change the object itself.
Example:
public class Program
{
static void Main(string[] args)
{
Foo foo = new Foo(1);
Console.WriteLine(foo.Bar);
// This will change foo.Bar
ChangeFoo(foo, 5);
Console.WriteLine(foo.Bar);
// Does not change foo
DoesNotChangeFoo(foo, 10);
Console.WriteLine(foo.Bar);
Console.Read();
}
static void ChangeFoo(Foo foo, int newValue)
{
// Since it receives a copy of the reference to Foo, it actually changes foo.Bar value
foo.Bar = newValue;
}
static void DoesNotChangeFoo(Foo foo, int newValue)
{
// Since it receives a copy of the reference to foo, it only updates this method's reference, not changing the caller's reference
foo = new Foo(newValue);
}
}
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
public int Bar { get; set; }
}
With ref
If you wanted to change the caller's object reference, you would need to pass the actual reference used by the calle's, that's when you use the ref keyword.
Example:
public class Program
{
static void Main(string[] args)
{
Foo foo = new Foo(1);
Console.WriteLine(foo.Bar);
// This will change foo's object reference
ChangeFooObjectReference(ref foo, 15);
Console.WriteLine(foo.Bar);
Console.Read();
}
static void ChangeFooObjectReference(ref Foo foo, int newValue)
{
// SInce you are receiving the actual reference used by the caller, you actually change it's own reference
foo = new Foo(newValue);
}
}
public class Foo
{
public Foo(int bar)
{
Bar = bar;
}
public int Bar { get; set; }
}
Your case
As you correcly assumed, the main cause of your problem is this line:
filterSource.SubFilters = ExtractFiltersByAType(filterSource.SubFilters, IsStandard);
This line actually changes this object's SubFilters.
But it's worth noting that you may have some bigger problems in you Clone method.
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter);
}
return copyOfFilters;
}
This method return's a new List, but the content of that list is exactly the same as the argument's. This means that, if you change any of the object's contained in the object used as an argument, you change it in the new List too.
Here's an example of what's happening.
static void Main(string[] args)
{
List<Foo> foos = new List<Foo>();
foos.Add(new Foo(2));
List<Foo> newFoo = CreateNewFoo(foos);
Console.WriteLine(newFoo.First().Bar);
foos.First().Bar = 5;
// Since we changed the first object of the old list, and it is the same object in the new list, we will get the new result.
Console.WriteLine(newFoo.First().Bar);
Console.Read();
}
static List<Foo> CreateNewFoo(List<Foo> foos)
{
List<Foo> newFoos = new List<Foo>();
foreach(Foo foo in foos)
{
newFoos.Add(foo);
}
return newFoos;
}
I would suggest implementing the ICloneable interface in your IReportFilter interface, and each concrete class implementing IReportFilter.
ICloneable implements a single method Clone(), which returns an object. This method should create a new instance of the same class in which it's implemented, containing a new object identical to the current object. Than you would change your method to:
private List<IReportFilter> CloneFilters(List<IReportFilter> myFilters)
{
List<IReportFilter> copyOfFilters = new List<IReportFilter>();
foreach (var myFilter in myFilters)
{
copyOfFilters.Add(myFilter.Clone() as IReportFilter);
}
return copyOfFilters;
}
As for implementing the ICloneable inteface, refer to this question:
Proper way to implement ICloneable
Edit
As mentioned by user muratgu in the question comments, your CloneFilter method is doing a shallow copy of your list, what you are looking for is a deep copy. That could be implemented with the aforementioned ICloneable interface.
The only thing that ref does is determine whether the method receiving the parameter can modify the variable passed to it. In the case of an object, that means setting the variable to a different object or setting it to null.
But even if you don't use ref, the method you pass the parameter to can can set its properties or call methods which modify the state of that object. That's normal and expected. When you pass an object to another method, it's not just so that the other method can read its properties. You might also want that method to operate on that object in some way that modifies it.
The simplest example is a List<T>. If you pass a List<string> to another method - without using ref - that other method can add items to the list, modify items in the list, clear the list, etc.
The only difference with using ref is that if the method you pass the variable to sets the variable to a different list or sets it to null, it's going to modify the variable in the method that passed the argument.
Most of the time we don't want a method we call to completely replace the variable we pass in. If we wanted a new object we'd write a function that returns an object, not one that replaces the variable we passed in.

Possible to create list from class of public strings in c#

I am using C# to create an app and want to be able to easily do a foreach() loop through my 120 strings that i have. All these strings are constructed as below:
public class _currentweekapi
{
public string _1_artist { get; set; }
public string _2_artist { get; set; }
public string _3_artist { get; set; }
...
}
Is it possible to be able to do a foreach() loop to loop through all the values. The strings are set automatically from a JSON feed so i cannot get the values straight into a list.
I have read up on the topic and understand there are ways through 'Reflection' but I am new to C# and reading up on it doesn't make any sense to me.
Could someone please help me with a solution to this stating exactly how I could implement this?
Using reflection is quite easy - try this for a start
var properties = typeof(_currentweekapi).GetProperties();
foreach(PropertyInfo info in properties) {
if (info.Name.EndsWith("artist") {
// do something amazing
}
}
see - http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx for more information
The first thing you should be trying to do, is NOT store the strings in explicitly named properties, as your class definition appears to be doing. The most appropriate scenario would seem to be a List<string> type property that can hold them all in your class. This would also mean you already have your list ready to go.So, if you are able to, change the class, or use another, which accepts the JSON feed, and uses .Add() on a property of type List<string>, rather than explicitly setting 120 properties.
Like this:
public class ArtistData
{
public List<string> Artists{ get; set; }
public ArtistData()
{
this.Artists = new List<string>(0);
}
public void PopulateArtists(string jsonFeed)
{
// here something desrializes your JSON, allowing you to extract the artists...
// here you would be populating the Artists property by adding them to the list in a loop...
}
}
Then, you have your list in the property Artists, and can use the list directly, or return it by doing:
string[] artists = myInstance.Artists.ToArray();
However, you seem to have indicated that you cannot change the fact that they end up served to you as individual properties in the class you showed us, so...
Assuming you have no choice, but to start from a class such as the one you showed, here is a way you could do a loop through all those values, just as you asked for, all that will be required is that you pass your class instance into one of the methods below as the one parameter that each requires:
// this will return a list of the values contained in the properties...
public List<string> GetListFromProperties<T>(T instance)
{
Type t = typeof(T);
PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// As a simple list...
List<string> artists = new List<string>(props.Length);
for (int i = 0; i < props.Length; i++)
{
if(!props[i].Name.Contains("_artist")){ continue; }
artists.Add(props[i].GetValue(instance, null).ToString());
}
return artists;
}
// this will return a dictionary which has each artist stored
// under a key which is the name of the property the artist was in.
public Dictionary<string,string> GetDictionaryFromProperties<T>(T instance)
{
Type t = typeof(T);
PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// As a dictionary...
Dictionary<string,string> artists = new Dictionary<string,string>(props.Length);
for (int i = 0; i < props.Length; i++)
{
if(artists.ContainsKey(props[i].Name) || !props[i].Name.Contains("_artist")){ continue; }
artists.Add(props[i].Name, props[i].GetValue(instance, null).ToString());
}
return artists;
}
Either one should help, but do not use the dictionary one, as it entails more overhead, unless you really need to know the name of the property each artist came from as well, in which case it is more helpful than a simple list.Incidentally, since the methods are generic, that is, they accept a parameter of type T, the same methods will work for ANY class instance, not just the one you are struggling with now. REMEMBER: although it may appear extremely convenient to you at the moment, this is not necessarily the best way to approach this. Better than this, would be the initial suggestions I made for re-working the class altogether so this sort of thing is not required.
You can do something like this:
List<string> list = new List<string>();
foreach (var property in _currentweekapi.GetType().GetProperties())
{
list.Add(property.GetValue(_currentweekapi, null));
}
If you can assure that there are no other types of properties then this will work:
var list =
typeof(_currentweekapi)
.GetProperties()
.Select(pi => (string)pi.GetValue(x, null))
.ToList();

C# Deferred Property Setting

I am working on a project where I need to queue up a number of property changes. Let say I have:
public class foo
{
string bar { get; set; }
int bar1 { get; set }
}
I want to have some code that looks like:
//Store value set actions in a queue
foo.SetValue(bar, "abc");
foo.SetValue(bar1, 123);
//Preview changes
foreach(Item change in foo.ChangeQueue)
{
Console.Write(change.PropertyName.ToString());
Console.Write(change.Value.ToString());
}
//Apply actions
foo.CommitChanges();
What is the best way to accomplish this?
You can use Dictionary<string,object> as ChangeQueue to store your values.
You can iterate as,
foreach(KeyValuePair<string,object> item in ChangeQueue){
Console.WriteLine(item.Key);// name of property
Console.WriteLine(item.Value); // value of property
}
public void SetValue(string name, object value){
PropertyInfo p = this.GetType().GetProperty(name);
// following convert and raise an exception to preserve type safety
ChangeQueue[name] = Convert.ChangeType(value,p.PropertyType);
}
public void ApplyChanges(){
foreach(KeyValuePair<string,object> item in ChangeQueue){
PropertyInfo p = this.GetType().GetProperty(item.Key);
p.SetValue(this, item.Value, null);
}
}
"Type-safe" version which uses callbacks. This will not automatically remove duplicate-settings. It also does not use reflection and so property-name errors will fail on compilation. This method could be expanded to require a "name" and remove duplicates by using a Dictionary backing (as per Akash's answer) or allow the "setter" to return a value (such as success or failure or the old value, or whatnot).
interface Setter {
void Apply();
}
class Setter<T> : Setter {
public T Data;
public Action<T> SetFn;
public void Apply() {
SetFn(Data);
}
}
List<Setter> changeQueue = new List<Setter>();
void SetValue<T>(Action<T> setFn, T data){
changeQueue.Add(new Setter<T> {
Data = data,
SetFn = setFn,
});
}
void ApplyChanges(){
foreach (var s in changeQueue){
s.Apply();
}
}
// .. later on
SetValue(x => System.Console.WriteLine(x), "hello world!");
ApplyChanges();
This method can also be trivially used "outside" the objects being monitored because all the operations are in potential closures.
Happy coding.

Categories