Ok so I have been up and down the internet looking for a solution to this question. I think my title is maybe no to informative so some background.
I have the following classes:
public class foo { public string Name { get; set; } }
public class foo1 { public string Name { get; set; } }
public class foo2 { public string Name { get; set; } }
public class foo3 { public string Name { get; set; } }
public class foo4 { public string Name { get; set; } }
public class foo5 { public string Name { get; set; } }
public class goo
{
public string Desc { get; set; }
public foo f { get; set; }
public foo1 f1 { get; set; }
public foo2 f2 { get; set; }
public foo3 f3 { get; set; }
public foo4 f4 { get; set; }
}
So now my question, Using Reflection, how can I get to the value of foo.Name when only having a reference to goo.
The normal Reflection code is:
goo g = new goo();
PropertyInfo pInfo = g.GetType().GetProperty("Name");
string Name = (string)pInfo.GetValue(g, null);
So the above code is how you get a property from the goo class. But now how do you get the value of foo.Desc?
I tried the following which doesn't work:
goo g = new goo();
PropertyInfo pInfo = g.GetType().GetProperty("f");
PropertyInfo pInfo2 = pInfo.PropertyType.GetProperty("Desc");
string Name = (string)pInfo2.GetValue(pInfo.PropertyType, null);
Unfortunately I get a Mismatched object error which I can understand because I am trying to use the property type and not the actual instance of the foo class. I also tried to fins a way to instantiate an object from the propertyinfo but if there is a way then it eludes me. I could do something like this:
goo g = new goo();
PropertyInfo propInfo = g.GetType().GetProperty("f");
object tmp;
propInfo.SetValue(g, Convert.ChangeType(new foo(), propInfo.PropertyType), null);
tmp = g.f;
This works but besides having to hard code the class, that is creating a new instance and therefore of now use to me.
As I say I have been up and down looking for a solution. Everything I have found is basically variants on the "get a value of a property of a class" theme but nothing about going another level deeper.
Can anyone help? Is this even possible because I would really like to stay away from hard coding.
EDIT: I have edited the class to more accurately represent what I am working with. Based on the comments below, I am getting the names of the foo instances from a database and that is why I am using Reflection or want to use Reflection instead of hard coding 30+ switch statements.
EDIT: Also I don't know before runtime which foo classes will be populated with data. Also each foo class is different. Unlike my example where each foo class has a string property, in my project each class has a different design which mirrors the database.
EDIT: So Ulugbek Umirov gave the answer. I just didn't see it immediately. Below my implementation so as to maybe help others in the future.
foreach (PropertyInfo pInfo in _standard.GetType().GetProperties())
{
if (_fullDataModel.ClassDefinitions.Contains(pInfo.Name))
{
PropertyInfo _std_pinfo = _standard.GetType().GetProperty(pInfo.Name);
object g = _std_pinfo.GetValue(_standard, null);
PropertyInfo props = g.GetType().GetProperty("showMe");
bool showMe = (bool)props.GetValue(g, null);
if (showMe)
{
string tblName = _fullDataModel.ClassDefinitions[pInfo.Name]. PropertyGroupDefinitions.Where(p => p.TransactionsTable != true).First().Token;
// Use tblName to build up a dataset
}
}
}
This does exactly what I wanted.
Thank you.
Given your current code you can do the following:
goo g = new goo();
g.f = new foo { Name = "Hello" };
PropertyInfo pInfo = g.GetType().GetProperty("f");
object f = pInfo.GetValue(g);
PropertyInfo pInfo2 = f.GetType().GetProperty("Name");
string name = (string)pInfo2.GetValue(f);
You can set arbitrary property as well:
goo g = new goo();
PropertyInfo pInfo = g.GetType().GetProperty("f");
object f = Activator.CreateInstance(pInfo.PropertyType);
PropertyInfo pInfo2 = f.GetType().GetProperty("Name");
pInfo2.SetValue(f, "Hello");
pInfo.SetValue(g, f);
Related
I'm working on a solution for being able to see if 2 objects are a match. All that matters is that for the 2 objects, if they share a property, the values of those properties are a match
For the two classes SC and DI, the Test property correctly evaluate to True. But the Little class will evaluate to false since SC and DI use different instances of the Little object, even though the information of the Little class is correct.
namespace HelloWorld
{
using System;
using System.Reflection;
public class Little
{
public string L1 { get; set; }
public string L2 { get; set; }
}
public class Solution
{
public string Name { get; set; }
public string Test { get; set; }
public Little L {get; set; }
}
public class Device
{
public string Name { get; set; }
public string Test { get; set; }
public Little L {get; set; }
}
public class Program
{
static void Main(string[] args)
{
var Li = new Little {
L1 = "L1111",
L2 = "L2222"
};
var Li2 = new Little {
L1 = "L1111",
L2 = "L2222"
};
var SC = new Solution {
Name = "SC",
Test = "T1",
L = Li2
};
var DI = new Device {
Name = "DI",
Test = "T1",
L = Li
};
foreach (var property in SC.GetType().GetProperties())
{
var deviceProperty = DI.GetType().GetProperty(property.Name);
if (deviceProperty != null)
{
if (deviceProperty.GetValue(DI) != property.GetValue(SC))
{
// it is registered as flase since DI and SC have different Little objects - even though thouse object values are the same
// Best way to determine if the values are of type Little (or any other me-defined class) - because wont .IsClass evaluate string to True as well?
// What if there was another class inside Litte? Do I have to recursively / loop until I can see everything is a string or int?
}
else
{
Console.WriteLine("We have a match");
}
}
}
}
}
}
GetValue returns an object, call a ToString() and then do "string comparison". As it is, your code compares two objects and so doesn't match.
If you expect properties of different types, then you need to override Equals to do custom comparison.
I have different classes sharing some properties of same type and name. I wish to assign same property values to each other. I explain my intention better in comments in the following pseudo-code. Is it possible in C#?
Ponder that there are a plethora of common properties but in unrelated classes, must we assign them one-by-one?
Second case is about sharing same properties but some of them may be nullable, who knows!
Side note: the classes already exist, cannot be altered, touched. Kinda sealed.
Can't it be done using nameofoperator and two for loops? Compare property names if matched, assign?
using System;
namespace MainProgram
{
class HomeFood
{
public DateTime Date { get; set; }
public string food1 { get; set; }
public string food2 { get; set; }
public int cucumberSize { get; set; }
}
class AuntFood
{
public string food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime Date { get; set; }
public string food1 { get; set; }
// extra
public double? length { get; set; }
}
class GrandpaFood
{
public string? food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime? Date { get; set; }
public string food1 { get; set; }
// extra
}
static class Program
{
public static void Main(string[] args)
{
var home = new HomeFood
{
Date = new DateTime(2020, 1, 1),
food1 = "cucumber",
food2 = "tomato",
cucumberSize = 123
};
var aunt = new AuntFood();
/*
First case: same types
Expected for-each loop
assigning a class's property values
to other class's property values
or for-loop no matter
foreach(var property in HomeFood's properties)
assign property's value to AuntFood's same property
*/
var home2 = new HomeFood();
var grandpa = new GrandpaFood
{
Date = new DateTime(2020, 1, 1),
food1 = "dfgf",
food2 = "dfgdgfdg",
cucumberSize = 43534
};
/*
Second case: similar to first case
with the exception of same type but nullable
or for-loop no matter
foreach(var property in GrandpaFood's properties)
assign property's value to GrandpaFood's same property
we don't care if it is null e.g.
Home2's same property = property's value ?? default;
*/
}
}
}
Based on the comments in the questions, this is just to show how it can be done with reflection.
Disclaimer, this is just a very simplified example on how to use reflection to sync properties. It does not handle any special cases (modifiers, read only, type mismatch, etc)
I would strongly suggest to use automapper to achieve the qp goals.
public class Type1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class Type2
{
public string Property1 { get; set; }
public string Property3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var t1 = new Type1 { Property1 = "Banana" };
var t2 = new Type2();
var properties1 = typeof(Type1).GetProperties().ToList();
var properties2 = typeof(Type2).GetProperties().ToList();
foreach(var p in properties1)
{
var found = properties2.FirstOrDefault(i => i.Name == p.Name);
if(found != null)
{
found.SetValue(t2, p.GetValue(t1));
}
}
Console.WriteLine(t2.Property1);
}
}
The short answer is, apply OOP. Define a base Food class and inherit from it in any specific food classes you have. You can put all the shared props in the base class.
public class Food
{
public string food2 { get; set; }
// other shared stuff
}
class GrandpaFood : Food
{
// other specific stuff
}
As others have said, use some of the Object Oriented properties, like inheriting a super class of implement an interface.
In case you go for inheritance, consider making the super class (the one you inherit from) abstract. This means that the super class itself cannot be instantiated, which greatly reduces the risk of violating the Liskov Substitutional Principle. Also it often reflects the real problem better. In your example, this would also be the case, as “food” is not an actual thing in the real world, but rather a group of things.
I have a little problem, below my code:
public class A
{
public string ObjectA { get; set; }
}
public void Run()
{
A a = new A();
a.ObjectA = "Test number 1";
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo myPropertyInfo = a.GetType().GetProperty("ObjectA", flags);
object myNewObject = myPropertyInfo.GetValue(a);// Here should be reference
a.ObjectA = "Test number 2";//After this line value myNewObject continued "Test number 1"
}
So my value myNewObject must be in output "Test number 2". Is there any way? It is this at all possible?
Wrong!
You're getting the string rather than the instance of A using reflection.
Changing A.ObjectA doesn't change the string reference. Actually, you're setting a different string to the backing string class field by the ObjectA property...
Auto-properties are syntactic sugar to avoid explicitly declaring class fields to properties which perform nothing when getting or setting their values:
// For example:
public string Text { get; set; }
// is...
private string _text;
public string Text { get { return _text; } set { _text = value; } }
Now turn your code into regular one (no reflection):
A a = new A();
a.ObjectA = "hello world";
object myNewObject = a.ObjectA;
// Do you think now that myNewObject should change...? :)
a.ObjectA = "goodbye";
Is there any way? It is this at all possible?
No.
Maybe you can simulate this behavior using a Func<T>...
Func<object> myNewObjectGetter = () => myPropertyInfo.GetValue(a);
Now, whenever you call myNewObjectGetter() you're going to get the most fresh value of the whole property. BTW, this still doesn't address the impossible!
Is there any way?
No. You can't put a reference to a property into an object variable. Such a variable can only hold a normal value, such as the string you put into it.
That answers the question as asked. You can clarify what you want to achieve and maybe we can suggest a better way.
Probably there is no solution but I show some code
public class MyRows
{
public string Key { get; set; }
public int Id { get; set; }
public object Val { get; set; }
}
public abstract class BasicDTO
{
public int? Id { get; private set; }
public PropertyInfo[] PropertyDTO;
protected Type myType;
public BasicDTO()
{
Load();
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyDTO = myType.GetProperties(flags);
}
}
public class CustomerDTO : BasicDTO
{
public string Surname { get; set; }
public string Phone { get; set; }
public CustomerDTO() { }
protected override void Load()
{
myType = typeof(CustomerDTO);
}
}
Now my basic method
public void Run(BasicDTO dto)
{
PropertyInfo pi = dto.PropertyDTO.Where(x => x.Name == "Surname").SingleOrDefault();
MyRows mr = new MyRows();
mr.Val = pi.GetValue(dto);//Here I need reference
}
When I change CustomerDTO.Surname my value mr.Val it must also be changed.
As I wrote above, it is probably impossible, but maybe anybody have a idea.
BTW: Value mr.Val I use only for binding (WPF). So maybe you have any other suggestions, how solve problem. I will be grateful for your help
In the list of objects of type
List<Configuracion.Models.v_cCfgDeclaraciones>
I would like retrive the name field, I would like to find the name of the field within the loop (foreach) better if I do not use reflection, how i can resolve this?
Put together something on Linqpad. Note that this does use reflection:
void Main() {
Test t = new Test();
Type tType = t.GetType();
foreach (PropertyInfo prop in tType.GetProperties()) {
prop.Name.Dump();
}
}
public class Test {
public string prop1 { get; set; }
public string prop2 { get; set; }
public Test() {
prop1 = "Property 1";
prop2 = "Property 2";
}
}
I don't know of a way you can get this information (short of hard-coding it in) without using reflection. Is there a reason you don't want to use reflection in this case?
My problem is quite simple
Suppose I have those class:
public class A
{
public Collection<B> B { get; set; }
public Collection<C> C { get; set; }
}
public class B
{
public int IntB { get; set; }
}
public class C
{
public string StringC { get; set; }
}
And I write a function:
public void GetValue(string fieldName){
A a = new A();
PropertyInfo infor = typeof(A).GetProperty(fieldName);
object obj = infor.GetValue(a,null);
}
My question is how can I turn obj to corresponding Collection, in this case is Collection<B> or Collection<C>, depending in fieldName value
Thank in advance
You can cast it:
var collection = (Collection<B>)(infor.GetValue(a,null));
EDIT:
if you use LINQ on the resulting collections, you may want to use OfType (link: http://msdn.microsoft.com/en-us/library/bb360913.aspx) and/or Cast (link: http://msdn.microsoft.com/en-us/library/bb341406.aspx)
Like
var collection = getCollection(a,null));
collection.OfType<B>.Select(b => b.IntB)....
You can't have a statically typed object in your method when you determine the property that will be called at runtime. But the runtime time of obj is of the actual type of your property.