C# accessing properties of an object from a collection class - c#

I am moving from VB to C#. I am trying to loop through a collection class which is a collection of data classes but I can't seem to get the actual values out of the data class properties(find the correct code to do so). I have a method that loops through the collection class(Contacts) and saves each record(Contact). I am using reflection because my method will not know if it is Contacts class or a Customer class and so forth. Here is my code in VB(watered down)
Public Function SaveCollection(ByVal objCollection as Object, ByVal TableName as string, ByVal spSave as string)
Dim objClass as Object
Dim propInfo as PropertyInfo
For Each objClass in objCollection
propInfo = objClass.GetType.GetProperty("TableFieldName")
Next
End Function
I am having problems in C# with the objClass.GetType.GetProperty("TableFieldName") line
Here is my C# code
public in SaveCollection(DictionaryBase objCollection, string TableName, string spSave)
{
PropertyInfo propInfo;
foreach (DictionaryEntry objClass in objCollection)
{
propInfo = objClass.GetType().GetProperty("TableFieldName")
}
}
The C# code keeps returning null. In my locals window I can see the proprties on the class on objClass and the value of the propery but I can seem to figure out how to access it through code. I used the DictionaryBase because that seems to closely match would I need to do. My data class(Contact) has a bunch or properties that match the field names in the database of the Contact Table. After I get the propInfo variable set I then set up my SQLParameter with the fieldname, datatype etc and then set the value to the propInfo.value.
Thanks for the help.

It looks like you are passing a different collection to the VB code and the C# code. My guess is that in the VB code you are passing the values of a dictionary and in C# you are passing the dictionary itself. Try changing the C# line to the following
propInfo = objClass.Value.GetType().GetProperty("TableFieldName");

I can't see why you're using a dictionary here - if it's just a collection, why not IEnumerable? Do you actually have a key here that you're storing things by?
DictionaryEntry will never have a TableFieldName property. Are you sure you don't need to take the value (or key) in the DictionaryEntry (using the Value or Key properties).
Do you really need to do this with reflection at all? Can't you use a generic method instead, after defining a common interface that has the TableFieldName property? For example:
public void SaveCollection<T>(IEnumerable<T> collection)
where T : ITableDescriptor
{
foreach (T element in collection)
{
string tableFieldName = element.TableFieldName;
// use the table field name here
}
}

you need to get the value afterwords. also note that GetValue returns an object, you can then cast it to string or int or whatever type the value you expect is in.
public in SaveCollection(DictionaryBase objCollection, string TableName, string spSave)
{
PropertyInfo propInfo;
foreach (DictionaryEntry objClass in objCollection)
{
object tempObj = objClass.Value;
propInfo = tempObj.GetType().GetProperty("TableFieldName");
object[] obRetVal = new Object[0];
object value = propInfo.GetValue(tempObj,obRetVal);
}
}
if you know that TableFieldName will be a string then change this line.
string value = propInfo.GetValue(tempObj,obRetVal) as string;

You're looking at the properties of the DictionaryEntry class. Not the class of the value in the particular key-value pair. Consider trying to use objClass.Value.GetType().GetProperty("TableFieldName").

Also consider switching to generics if you can. I think you should be able to do something like this:
VB:
Private l As List(Of MyClassName)
C#:
private List<MyClassName> l;
You may not want to use a list, but it's just an example. After that, accessing members of each item in the list can be done through intellisense. Often times generics can be faster too since you don't have to do any casting.

Related

How can I set a List<foo> field of an object with the value of List<dynamic>

I have been trying to set some field values of specific objects in C#.
For other reasons I need to construct a List of values from a string then I want to assign this to a field in an object.
The way I indicate the value type of the list is something like this in string
name:type{listItemName:listItemType{listItemValue}}
This list can be of any type, so it is undetermined until we reach conversion.
I am using
List<dynamic> ldy = new List<dynamic>();
foreach (string listElement in listElements)
{
if (listElement == "") continue;
Type leDataType = GetDataType(listElement);
string leData = GetDataRaw(listElement);
var leDynamic = ConstructDataObject(leDataType, leData, listElement);
ldy.Add(leDynamic);
}
Which ends up with the correct data and with the correct data type when I enquire, however when I am trying to use the resulting ldy list and assign it to a field, of course it acts as a List<object>
thus not allowing me to assign.
Finally so, I am using field.SetValue(targetObject, ldy); to assign the value to the field.
The error message I am getting is
ArgumentException: Object of type 'System.Collections.Generic.List`1[System.Object]' cannot be converted to type 'System.Collections.Generic.List`1[System.Int32]'.
Which to be clear, I do understand and I do understand why, however I dont really see how could I solve this issue, not by solving this nor by changing the fundaments of my code design.
Please help!
As #juharr suggested I have searched for solutions to do this with reflection.
Here is my solution:
private static IList GetTypedList(Type t)
{
var listType = typeof(List<>);
var constructedListType = listType.MakeGenericType(t);
var instance = Activator.CreateInstance(constructedListType);
return (IList)instance;
}

generate predictable unique string based on a generic objects content

Story
I'm trying to write a generic method which combines property names, types and content value to generate a unique string for the value held by the object passed.
The idea is to generate a unique SHA3-512 Hash based on the generated string sequence which can be used to compare objects on generic bases by looking at their content.
Example
Let's say we have a class like this ...
class MyClass {
private Int32 Id = 5;
public String Name = "some string";
protected DateTime CreateDate = DateTime.Parse("2017-08-21 15:00:07");
}
... and the mentioned method to generate the unique string
static String GetContentString<T>(T obj) where T : class {
...
}
In theory this should work somewhat like this:
var myObj = new MyClass();
var uniqueContentString = GetContentString(myObj);
Console.WriteLine(uniqueContentString);
>>> Id:Int32:5$Name:String:some string$CreateDate:DateTime:2017-08-21 15:00:07
Problem
I'm having difficulties building the GetContentString Method. This is what I have already:
Object obj = ... // given object
Type type = obj.GetType();
IList<PropertyInfo> propertyInfos = type.GetProperties().Where(x => x.CanRead).ToList(); // Marker #2
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo pi in propertyInfos)
{
sb.Append(pi.Name);
sb.Append(":");
sb.Append(pi.PropertyType.Name);
sb.Append(":");
sb.Append(pi.GetValue(obj) ?? "[ISNULL]"); // Marker #1
sb.Append(":");
}
return sb.ToString();
I tried running the method for a few different types of values like "some string" or 1234 (Int32) and ran into a few issues.
Given a string, the method call throws an exception of type System.Reflection.TargetParameterCountException and the message Parameter count mismatch at #1. I found out that an optional index can be passed to an overloaded version of pi.GetValue(..) which then returns one of the single letters. But how do you know when to stop? If you call an index which doesn't exist it throwns an exception of the type System.Reflection.TargetInvocationException. How do you get the value of a string object using reflection?
Given an integer value, the method call doesn't find any properties at #2. Which brings up the question of how to get the value of an integer object using reflection?
And also some general questions; do you guys think this is a good approach to get a unique string? Is reflection the way to go here? Is it even possible to write a generic solution to this problem?
Without looking at reflection, how about JSON serialization with something that .net framework is able to ?
Reflection isn't something extremely fast and you'll run into issues at the first unhandled exception.
Then, you should do that recursivly if your objects can contains complex properties, wich is not a problem with json serialization !

Using Reflection Foreach through IEnumerable or PropertyInfo[]

I've never worked with Reflection but I have a situation where I think it would be useful without being too expensive as a process. I have five different List<.object> where each one has different properties and I have a WebMethod that returns only one of these types of List<.object>'s based on a switch statement and passes the resulting list to a method that should iterate through the values and write them out to a StringBuilder that is passed back to the WebMethod. I did not want to have to write five different methods to iterate through each object and write out the string.
Looking through SO resulted in this code as a good start for the method (below) however, I don't understand the best way to get what I need after that. I tried using PropertyInfo[] but it threw errors while casting the object. IEnumerable doesn't throw errors but I can't seem to access the values either.
private StringBuilder generateString<T>(T obj) where T : class
{
//Trying to use PropertyInfo[] example
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo p in properties)
sb.Append("{0},{1}", p.Name, p.GetValue(obj,null));
//Trying to use IEnumerable example
IEnumerable enumerable = (IEnumerable)obj;
foreach(var x in enumerable)
sb.Append(x);
}
When I try to look at the values in the PropertyInfo[] array it shows me that it has three items, the last being my object type but I can't seem to find any of the actual values. It throws two errors TargetParameterCountException and TargetInvocationException.
However, when I look at the IEnumerable data of x it shows me what I expect to see except not in a format where I think can access the individual pieces, for example:
x
{Base.DataType.MyObject}
Age: 24
Name: Helen Smith
Cost: 24.00
Date: 2/25/2012
Which is the correct way to try and access this data? And how do I do it correctly?
It seems that you are passing an IEnumerable, not a single object.If so you need to iterate over the objects and get the type of each item, and properties separately:
if(obj is IEnumerable)
{
foreach(var item in (IEnumerable)obj)
{
Type type = item.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo p in properties)
sb.Append("{0},{1}", p.Name, p.GetValue(item,null));
}
}
What exactly do you mean by which is the best way to access the data? Are you just trying to access what is in the StringBuilder object or are you just needing to output the information in some form?
The way I have used reflection in the past to get the name and the value would be like this:
public StringBuilder generateString(List<object> objList)
{
StringBuilder sb = new StringBuilder();
int itemNumber = 0;
foreach(var obj in objList)
{
foreach (var prop in obj.GetType().GetProperties())
{
// i used the new line characters to increase readability for
// output to a console so just ignore them.
sb.Append("Item " + itemNumber);
sb.Append("\n");
sb.Append(prop.Name + ", " + prop.GetValue(obj, null));
sb.Append("\n");
}
}
return sb;
}
Viewing the output of this string builder object i saw this:
Item 1
Property1, value
Property2, value
Property3, value
Item 2
Property1, value
Property2, value
Property3, value
etc...
I hope this helped you.
If all the objects have the same type, which I believe is what you are doing. And you are passing in a List<T>, you can get the item type like so:
var listType = obj.GetType();
var itemType = listType.GetElementType();
That would give you itemType of MyObject if you passed in a List<MyObject>. From there you should be able to GetProperties as you were.
If the list is heterogeneous, then you will need to loop through and get the properties of each item as in #Selman22's answer.

Writing a certain generic method

I have a series of methods to write, but I think there is some commonality (well, I know there is). The method I'd like to see will take two things, an object and... well, I'm not so sure about the second one, but probably a string.
The object should be generic, although it can only be from a set list (in this case, that commonality seems to be that they inherit from both INotifyPropertyChanging and INotifyPropertyChanged interfaces).
The string should be the name of a property within the generic object. It should be checked to see if that property exists within that object before being put into use (it would be used as a way to compare the objects by that given property).
So I guess the process would be... generic object gets passed into method (along with property name string). A check to see if the object contains the property. If it does, continue and somehow have access to 'object.PropertyName' where 'PropertyName' was the supplied string.
I don't know if it's easy or possible or even wise, but I know that it would save me some time.
Thanks in advance for any advice you might be able to offer with this.
Edit: Thanks for the all the replies so far guys. Let me clarify some things:
Clarification of 'access'
When I said, "... and somehow have access to 'object.PropertyName'", what I meant was that the method should be able to use that property name as if it were just a property of that object. So, let's say the string passed in was "ClientName", there would be the ability to read (possibly write, although at the moment I don't think so as it's just a check) object.ClientName, if it was determined that existed.
What I'm trying to do
I have a WCF service which accesses an SQL database using Linq. The objects I spoke of are entities, generated from the program SQLMetal.exe, so my objects are things like 'Client', 'User' and this sort of thing. I wrote a method which took a List of entities. This method added only those entities which did not exist within the collection (some could have been duplicates). It figured out which ones were duplicates by checking a property within the entity (which corresponds to data in a column of the database). It's that property which I figured might be variable.
It sounds like you don't really want to check if it's a certain type, and if that is so then you don't have to and its actually easier not to check the type. This shows how to check if the property exists and if it is readable and writeable and shows how to use it after it's found:
private void Form1_Load(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
PropertyInfo info = GetProperty(sb, "Capacity");
//To get the value of the property, call GetValue on the PropertyInfo with the object and null parameters:
info.GetValue(sb, null);
//To set the value of the property, call SetValue on the PropertyInfo with the object, value, and null parameters:
info.SetValue(sb, 20, null);
}
private PropertyInfo GetProperty(object obj, string property)
{
PropertyInfo info = obj.GetType().GetProperty(property);
if (info != null && info.CanRead && info.CanWrite)
return info;
return null;
}
I think only indexer properties can take parameters in C#. And I believe if you wrote properties in VB that take parameters and tried to reference that assembly in C# they would show up as methods instead of properties.
You could also write a function like this that would take 2 objects and a string for a property name and return the result of those properties matching:
private bool DoObjectsMatch(object obj1, object obj2, string propetyName)
{
PropertyInfo info1 = obj1.GetType().GetProperty(propertyName);
PropertyInfo info2 = obj2.GetType().GetProperty(propertyName);
if (info1 != null && info1.CanRead && info2 != null && info2.CanRead)
return info1.GetValue(obj1, null).ToString() == info2.GetValue(obj2, null).ToString();
return false;
}
Comparing the values of the properties might be tricky because it would compare them as objects and who knows how equality will be handled for them. But converting the values to strings should work for you in this case.
If you know the 2 objects are the same type then you can simplify it:
private bool DoObjectsMatch(object obj1, object obj2, string propetyName)
{
PropertyInfo info = obj1.GetType().GetProperty(propertyName);
if (info != null && info.CanRead)
return info.GetValue(obj1, null).ToString() == info.GetValue(obj2, null).ToString();
return false;
}
I think you're looking for something like:
public void SomeMethod<T>(T object, string propName)
where T : INotifyPropertyChanging, INotifyPropertyChanged
(
var type = typeof(T);
var property = type.GetProperty(propName);
if(property == null)
throw new ArgumentException("Property doesn't exist", "propName");
var value = property.GetValue(object, null);
)

How to access a property for each memebr of an indexer using reflection in C# .net 2.0

I have the following method:
object GetIndexer()
The result of the method is an indexer of the type:
SomeCollection<T>
Now T can be anything, by I know that each T extends the type Y.
I tried casting
SomeCollection<Y> result= (SomeCollection<Y>) GetIndexer()
It didn't work.
What I need to know is how to access a property for each item in the indexer SomeCollection using Reflection?
Use GetType() for each item, then call either GetProperties() or GetProperty(propertyName) to obtain the PropertyInfo. With this, you then call GetValue() passing in your object.
An example:
List<object> objects = new List<object>();
// Fill the list
foreach (object obj in objects)
{
Type t = obj.GetType();
PropertyInfo property = t.GetProperty("Foo");
property.SetValue(obj, "FooFoo", null);
Console.WriteLine(property.GetValue(obj, null));
}
Some context would be helpful, but it sounds like you've got something like
class Foo<T> where T : Y {
object GetIndexer() { /* ... */ }
}
In that case, why not just
SomeCollection<Y> GetIndexer() { /* */ }
That way, no cast is required.
But I am a little confused by your use of the term 'indexer'. An indexer for a C# is a way of overloading the [] operator for a type, so that you can do things like:
MyCollectionType c = new MyCollectionType()
c["someValue"]
They are defined like so:
class MyCollectionType {
public string this [string index] // This particular indexer maps strings to strings, but we could use other types if we wished.
get {} // Defines what to do when people do myCollection["foo"]
set {} // Defines what to do when people do myCollection["foo"] = "bar"
}
An object of type SomeCollection<Y> isn't an indexer, it's a collection.
Is SomeCollection<T> enumerable? If so you could do
var transformed = new SomeCollection<Y>();
var someObjectCollection = (IEnumberable)GetIndexer();
foreach (var someObjectin someObjectCollection);
transformed.Add((Y)someObject);
Or wait until C# 4.0 gives us more covariance and contravariance options.
To loop through a list is the process of enumeration. It is easiest with an Enumerator. That is (in C#): anything that implements IEnumberable.
If the object you are trying to loop through is one of your own making, I recommend implementing IEnumberable. If it isn't, can you give more info about this specific 3rd party object? Maybe there are others who've also needed to use it in this way and maybe their work can be found by one of us online.

Categories