I'm using the following code to output values of properties:
string output = String.Empty;
string stringy = "stringy";
int inty = 4;
Foo spong = new Foo() {Name = "spong", NumberOfHams = 8};
foreach (PropertyInfo propertyInfo in stringy.GetType().GetProperties())
{
if (propertyInfo.CanRead) output += propertyInfo.GetValue(stringy, null);
}
If I run this code for the int, or for the Foo complex type, it works fine. But if I run it for the string (as shown), I get the following error on the line inside the foreach loop:
System.Reflection.TargetParameterCountException: Parameter count mismatch.
Does anyone know what this means and how to avoid it?
In case anyone asks 'why are you enumerating through the properties of a string', eventually I hope to create a generic class which will output the properties of any type passed to it (which might be a string...).
In this case, one of the string's properties is the indexer for returning the character at the specified location. Thus, when you try to GetValue, the method expects an index but doesn't receive one, causing the exception.
To check which properties require index you can call GetIndexParameters on the PropertyInfo object. It returns an array of ParameterInfo, but you can just check the length of that array (if there are no parameters, it will be zero)
System.String has an indexed property that returns the char in the specified location. An indexed property needs an argument (index in this case) therefore the exception.
Just as reference... if you want to avoid the TargetParameterCountException when reading properties values:
// Ask each childs to notify also, if any could (if possible)
foreach (PropertyInfo prop in options.GetType().GetProperties())
{
if (prop.CanRead) // Does the property has a "Get" accessor
{
if (prop.GetIndexParameters().Length == 0) // Ensure that the property does not requires any parameter
{
var notify = prop.GetValue(options) as INotifyPropertyChanged;
if (notify != null)
{
notify.PropertyChanged += options.OptionsBasePropertyChanged;
}
}
else
{
// Will get TargetParameterCountException if query:
// var notify = prop.GetValue(options) as INotifyPropertyChanged;
}
}
}
Hope it helps ;-)
Related
So I have 2 classes, both have identical Property names. One class contains different variables: int, strings, bool and DateTime The second class contains only 1 int and the rest are all strings.
Now I want to loop through all the properties, get the value from class1, encrypt that data and save it as a string in obj2, then return it to the main form (to save it in a database later).
public PersoonEncrypted EncryptPersonClass(Class1 object1)
{
PersoonEncrypted persEncrypt = new PersoonEncrypted(); //second class obj
Type type = object1.GetType();
PropertyInfo[] properties = type.GetProperties();
Type type2 = persEncrypt.GetType();
PropertyInfo[] properties2 = type.GetProperties();
foreach (var bothProperties in properties.Zip(properties2, (obj1, obj2) => new { Obj1 = obj1, Obj2 = obj2 }))
{
string value = "";
value = bothProperties.Obj1.GetValue(object1) as string;
if (!string.IsNullOrWhiteSpace(value))
{
string encryptValue = Encrypt(value);
if ((bothProperties.Obj2 != null) && (bothProperties.Obj2.PropertyType == typeof(string)))
{ //!= null check has no effect at all
bothProperties.Obj2.SetValue(persEncrypt, encryptValue, null); //errorLine
}
}
}
return persEncrypt;
}
That is what I came up with until now.
I have, of course, searched for other solutions like this one. This, after applying some own changes, didn't return any errors, but it didn't save any encrypted strings into the class persEncrypt. What I concluded was, from that test, is that it was testing if the value in the second class(persEncrypt in my example) from the particular property was null, while it shouldn't do that, it should make a new instance of that variable and save it in the object class, but removing that check gave me the same error.
you're just .Zip-ing the two lists of PropertyInfo objects, which simply iterates through both lists and doesn't check or sort for any sort of matching. This could result in erroneous behavior depending on the order in which properties appear - consider using a .Join instead to match property names.
This code doesn't check for an indexer on the property before attempting to assign to it without one - any indexed property which is of type string will make it to this point and then throw an exception when you try to set it.
Because this code is calling into Properties, there's the possibility an exception is being thrown by the code of the Property itself. This is where a StackTrace from your exception could reveal much more about what's happening.
Your code also checks for a property of type string directly - when using reflection you should use IsAssignableFrom instead in order to allow for inherited types, though that is unlikely the issue in this one case.
First, forgive me if this question is redundant to other information on the site. I've looked through other postings regarding reflection, but can't seem to make any sense of them in terms of what I'm doing.
The idea is to pass a method a class, then loop through that objects properties and assign it's value to a return value. The return value will later be used to create an SQL statement on the fly. I've created the loop using PropertyInfo, and I'm able to tell the type of property based on a switch statement. The problem is once I'm in the specific case for the property, I'm having a difficult time trying to figure out how to assign that particular properties value to the list to be returned later. Here is my code, thanks in advance for any help.
public List<string> BuildParametersFromObject(clsBook oBook, clsStudent oStud)
{
List<string> lstRetVal = new List<string>();
string sTemp = "";
string sPropType = "";
int ii;
if (oBook == null)
{
PropertyInfo[] ObjProperties = typeof(clsStudent).GetProperties();
foreach (PropertyInfo StudProp in ObjProperties)
{
ii = 0;
sPropType = Convert.ToString(StudProp.PropertyType);
switch (sPropType)
{
case "System.String":
lstRetVal.Add(StudProp.GetValue(clsStudent, null));
break;
case "System.Int32":
break;
case "System.Decimal":
break;
case "System.Boolean":
break;
case "System.DateTime":
break;
}
}
}
return lstRetVal;
}
Your code does not compile, since
StudProp.GetValue(clsStudent, null)
requires it's first parameter to be an object and you're giving that method what is most surely a class symbol.
Second of all, I was trying to understand what you intended to do with your two parameters:
clsBook oBook
clsStudent oStud
From what you wrote there I understand that if oBook is not null, then you go about with the adding of results to your returning list. I'll leave it at that for now.
The second biggest question mark is whether you need the oStud parameter whatsoever.
Say you solve the compilation problem, and your code does compile.
If you would try deleting the parameter altogether i.e.
public List<string> BuildParametersFromObject(clsBook oBook, clsStudent oStud)
becomes
public List<string> BuildParametersFromObject(clsBook oBook)
you would notice that your method still compiles, hence you're not using oStud.
You're enumerating the properties of the clsStudent
If you're not sure as to what I'm talking about then you can simply do the following:
quick resolution: apply the following modifications (just below)
longterm resolution: read more about OOP and Reflection:
http://www.codeproject.com/Articles/22769/Introduction-to-Object-Oriented-Programming-Concep
http://msdn.microsoft.com/en-us/library/vstudio/ms173183(v=vs.110).aspx
MODIFICATIONS
// prepare your little filter
private static readonly Type[] allowedTypes = new Type[] {
typeof(bool), typeof(string), typeof(int),
typeof(decimal), typeof(DateTime)
};
public List<string> BuildParametersFromObject(clsBook oBook, clsStudent oStud)
{
List<string> lstRetVal = new List<string>();
string sTemp = "";
string sPropType = "";
int ii;
if (oBook == null)
{
PropertyInfo[] ObjProperties = typeof(clsStudent).GetProperties();
foreach (PropertyInfo StudProp in ObjProperties)
{
// if the property's type is not one of the 5 magnificence
// simply jump to the next cycle of the foreach loop
if (!allowedTypes.Contains(StudProp.PropertyType))
continue;
object pureValue = StudProp.GetValue(oStud);
// what you were using is not ok: StudProp.GetValue(clsStudent)
// because you're trying to retrieve the value of a property
// defined by the clsStudent class
// and in the process you also need to point out an instance
// of that particular class, but instead of doing that
// you were pointing out an instance of Type i.e. clsStudent itself
string stringValue = pureValue + "";
// simply concatenate the pureValue with a blank string
// (allowing imminent nulls to be displayed as empty strings instead of crashing your thread with NullReferenceException)
lstRetVal.Add(stringValue);
// just add your stringified value to the list
}
}
return lstRetVal;
}
EXPLANATION
What I've done is I've modified your explicit filtering (using switch) to a primitive array (a list of the types you wish to support in the adding of results) and by that I've also removed the unnecessary and completely hard to maintain "by Name comparison" of types. It's much cleaner to compare types for what they are: themselves. There can be RunninThruLife's in the world, but only one of them is you.
The biggest error was the fact that instead of getting the properties' values from the clsStudent instance you were getting them from clsStudent itself (which is a class).
In each of the other cases, just call ToString() on the result of GetValue().
StudProp.GetValue(oStud, null).ToString();
Obviously if you need specific formatting you would do that before adding to the list.
I have a requirement to copy ONLY populated values from one object that are not already populated in another object of the same type.
For example we are passed an object, it is only partially instantiated with data, we read the database to get a fully instantiated version of the object – however this may not have changes by the application committed to the database yet – hence we need to move any values from the database version into the passed in version of the object – without overwriting any values that may already exist in the passed in object (as these are the most up to date values).
The code below suggested by Adam Robinson in another post (see below very useful – thanks!) is a good starting point. However I need to extend this – as I only want to copy over values that are NOT already populated on the target object (i.e. need to check the destProperty is not null). However as an added complication, there are internal complex types declared within the object passed in, this code copies the high level sub groups over without going into the individual properties of the sub groups (i.e. any variables declared with the Root cdt I can try and check for null, but all the fields in sub cdts are simply copied over without going through the individual fields).
Any help would be greatly appreciated.
public static void CopyPropertyValues(object source, object destination)
{
var destProperties = destination.GetType().GetProperties();
foreach (var sourceProperty in source.GetType().GetProperties())
{
foreach (var destProperty in destProperties)
{
if (destProperty.Name == sourceProperty.Name &&
destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
destProperty.SetValue(destination, sourceProperty.GetValue(
source, new object[] { }), new object[] { });
break;
}
}
}
}
First you need to determine what populated means in your book.
Once you determine that, write you own version of the following IsDefaultValue method.
What I have written will answer with true in the following manner:
if it's a bool then it needs to have the false value
if it's an int then it needs to be 0
if it's any class then needs to be null
etc
So here's my version of the method:
public static bool IsDefaultValue(object #object) {
if (null == #object)
return true;
else if (#object.GetType().IsValueType) {
var isDefault = Activator.CreateInstance(#object.GetType()).Equals(#object);
return isDefault;
} else
return false;
}
Then, presuming that you're interested only in the non indexer properties, that you have no hierarchies and that you will always call this method with objects of the same type,
you could just filter out those properties which are not default in the source but are default in the destination.
Then you could traverse the instance graph recursively for those cases when you have a non-default value in your destination properties.
Please not that what I have written here is just a demonstration of how you might be able to accomplish your task. There are intricate details in your particular scenario which you need to address yourself as they are not obvious from you question.
For instance, I assumed it would be a good idea to add a stop condition for the recursive traversal (the remainingDepth parameter)
public static void CopyPropertyValues(object source, object destination, int remainingDepth = 3) {
// we've reached the farthest point we're allowed to go to
// anything beyond this point won't be affected by this method
if (remainingDepth == 0)
return;
// just a check to make sure the following lines won't backfire
if ((null == source) || (null == destination))
throw new ArgumentNullException();
// we'll need to also check that the 2 objects are of the same type
var type = source.GetType();
if (destination.GetType() != type)
throw new ArgumentException("The two objects should be of the same type");
var properties = type.GetProperties()
// just filter out the properties which are indexers (if any)
// and also those properties which are read or write only
.Where(property => (property.GetIndexParameters().Length == 0) &&
property.CanRead && property.CanWrite);
foreach (var property in properties) {
var sourceValue = property.GetValue(source, null);
var destValue = property.GetValue(destination, null);
if (!IsDefaultValue(sourceValue))
if (IsDefaultValue(destValue))
property.SetValue(destination, sourceValue, null);
else
if (sourceValue.GetType() == destValue.GetType())
CopyPropertyValues(sourceValue, destValue, remainingDepth - 1);
}
}
Please note that property enumeration is needed just once since the objects (as you said it yourself in the comments section) are of the same type.
Also please beware of Reflection when performance is of importance.
Well, I need to repeat same code for many properties.
I've seen examples taking Action delegates, but they don't fit quite well here.
I want something like this: (see explanation below)
Dictionary<Property, object> PropertyCorrectValues;
public bool CheckValue(Property P) { return P.Value == PropertyCorrectValues[P]; }
public void DoCorrection(Property P) { P.Value = PropertyCorrectValues[P]; }
.
I want to have a dictionary containing many properties and their respective "correct" values. (I know it's not well declared, but that's the idea). Properties are not necessarely inside my class, some of them are in objects of different assemblies.
A method bool CheckValue(Property). This method must access the actual value of the property and compare to the correct value.
And a method a void DoCorrection(Property). This one sets the property value to the correct value.
Remember I have many of those properties, I wouldn't like to call the methods by hand for each property. I'd rather iterate through the dicionary in a foreach statement.
So, the main question is in the title.
I've tried the by ref, but properties don't accept that.
Am I obligated to use reflection??? Or is there another option (if I need, reflection answer will be accepted as well).
Is there anyway I can make a dictionary with pointers in C#? Or some kind of assignment that changes the value of variable's target instead of changing the target to another value?
Thanks for the help.
You can do this using reflection. Get a list of the properties on the object of interest with typeof(Foo).GetProperties(). Your PropertyCorrectValues property can have type IDictionary<PropertyInfo, object>. Then use the GetValue and SetValue methods on PropertyInfo to perform the desired operations:
public bool CheckProperty(object myObjectToBeChecked, PropertyInfo p)
{
return p.GetValue(myObjectToBeChecked, null).Equals(PropertyCorrectValues[p]);
}
public void DoCorrection(object myObjectToBeCorrected, PropertyInfo p)
{
p.SetValue(myObjectToBeCorrected, PropertyCorrectValues[p]);
}
In addition to Ben's code I'd like to contribute the following code fragment:
Dictionary<string,object> PropertyCorrectValues = new Dictionary<string,object>();
PropertyCorrectValues["UserName"] = "Pete"; // propertyName
PropertyCorrectValues["SomeClass.AccountData"] = "XYZ"; // className.propertyName
public void CheckAndCorrectProperties(object obj) {
if (obj == null) { return; }
// find all properties for given object that need to be checked
var checkableProps = from props
in obj.GetType().GetProperties()
from corr in PropertyCorrectValues
where (corr.Key.Contains(".") == false && props.Name == corr.Key) // propertyName
|| (corr.Key.Contains(".") == true && corr.Key.StartsWith(props.DeclaringType.Name + ".") && corr.Key.EndsWith("." + props.Name)) // className.propertyName
select new { Property = props, Key = corr.Key };
foreach (var pInfo in checkableProps) {
object propValue = pInfo.Property.GetValue(obj, null);
object expectedValue = PropertyCorrectValues[pInfo.Key];
// checking for equal value
if (((propValue == null) && (expectedValue != null)) || (propValue.Equals(expectedValue) == false)) {
// setting value
pInfo.Property.SetValue(obj, expectedValue, null);
}
}
}
When using this "automatic" value correction you might also consider:
You cannot create a PropertyInfo object just by knowing the property name and independently of the declaring class; that's why I chose string for the key.
When using the same property name in different classes then you might need to change the code that is doing the actual assignment because the type between the correct value and the property type might differ.
Using the same property name in different classes will always perform the same check (see point above), so you might need a syntax for property names to restrict it to a specific class (simple dot notation, doesn't work for namespaces or inner classes, but might be extended to do so)
If needed you can replace the "check" and "assign" part with separate method calls, but it might be done inside the code block as stated in my example code.
Not sure if this is possible, but here is what I am trying to do:
I want to have a dictionary that contains a mapping of a column index to a property name used to populate that index.
In my code I will loop through an array if strings and use the dictionary to look up which column it should map to.
My end result code would look like:
for(int index = 0; index < fields.Length)
{
fieldPropertyMapping[index] = StripQuotes(fields[index]);
}
To do what you're asking specifically, you'll have to use reflection (as you tagged your question) to do this. Have a look at the PropertyInfo class. I'm not entirely certain what your code is doing, but a general example of reflectively setting a property value would be:
object targetInstance = ...; // your target instance
PropertyInfo prop = targetInstance.GetType().GetProperty(propertyName);
prop.SetValue(targetInstance, null, newValue);
You could, however, pass an Action<T> instead, if you know the property at some point in the code. For example:
YourType targetInstance = ...;
Action<PropertyType> prop = value => targetInstance.PropertyName = value;
... // in your consuming code
prop(newValue);
Or, if you know the type when you call it but you don't have the instance, you could make it an Action<YourType, PropertyType>. This also would prevent creating a closure.
Action<YourType, PropertyType> prop = (instance, value) => instance.PropertyName = value;
... // in your consuming code
prop(instance, newValue);
To make this fully generic ("generic" as in "non-specific", not as in generics), you'll probably have to make it an Action<object> and cast it to the proper property type within the lambda, but this should work either way.
You have a couple of choices:
Use reflection. Store and pass a PropertyInfo object into the method and set it's value through reflection.
Create an ActionDelegate with a closure to that property and pass that into the method.
you can use reflection to get the properties for a class:
var properties = obj.GetType().GetProperties();
foreach (var property in properties)
{
//read / write the property, here... do whatever you need
}