Dynamically detect object properties in a dll passed from different project - c#

I am building a generic usercontrol. which will be a combination of one label and one textbox. This will be a class library project.
Text of the label and textBox will be bounded from outside projects. In the projects where I imported the dll.
I am trying to make the user control fully generic. For say, I will send any object, and a property name of the object to the dll. dll will read the object, get the object properties, get value for the sent parameter and act as another ready controls in win forms.
myControl1.DataBindings.Add("DataSource",object,"PropertyName1");
By just saying that, myController1 will get the value associated with PropertyName1 of the object and bind values with the textbox and label.

You wanna use reflection for this.
This is how you do it using an extension method, but you can easily make it a regular function if that's what you need. I tried to explain what it does with comments.
public static T GetProperty<T>(this object obj, string name)
{
Type type = obj.GetType(); // Get the object's type
PropertyInfo pinfo = type.GetProperty(name); // Get the property information for the specified property name
object ret = pinfo?.GetValue(obj); // If pinfo != null, get the value of the property on obj
return ret == null ? default(T) : (T)ret; // If ret is null, return the default value for T (for example, "" if T is string). If it isn't, return ret casted to T
}
Usage example:
Form1.Text == Form1.GetProperty<string>("Text"); // true

Related

Edit Method WPF | Dynamic Parameters

Situation
I'm trying to make an edit member method for my WPF application.
Basically, I have a list of members in my main class, I iterate through that list of members and find the member with the matching Username parameter, that works.
Then, once that username is found within the system (which it will be since the member needs to login with a valid one) I want to set the member."Whatever" paramater to whatever parameter the user chose to edit on the gui, with the new content the user has entered for that parameter.
public void editMember(string Username, string parameter, string newEntry)
{
foreach (BaseMember bm in members)
{
if (Username == bm.username)
{
bm.[parameter] = newEntry;
}
}
Problem
I don't want to do:
"member.club" and "member.firstname", or "member.street", since there are far too many parameters that can be edited by the user, and it's long winded "bad" code.
how can I do this in ONE line of code? since bm.[parameter] = newEntry; won't work?
More info
This method works if I use a static parameter, for instance, bm.memclub = newEntry; but I want the parameter to be dynamic.
You need to use Reflection:
foreach (BaseMember bm in members)
{
if (Username == bm.username)
{
Type type = bm.GetType();
PropertyInfo prop = type.GetProperty(parameter);
prop.SetValue (bm, newValue, null);
}
}
Reflection provides objects (of type Type) that describe your current object.You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties.

How to pass the current instance to FieldInfo.GetValue inside the instance?

I'm making an abstract class that I need to validate fields in it's descendants. All the validated fields are marked by an attribute and are suppose to be checked for nulls or empty. To do that I get all the fields in the class:
var fields = this.GetType().GetFields().Where(field => Attribute.IsDefined(field, typeof(MyAttribute))).ToList();
And then, for every FieldInfo I try to do this:
if (string.IsNullOrEmpty(field.GetValue(this).ToString()))
{
// write down the name of the field
}
I get a System.NullReferenceException: object reference not set to an instance of an object.
I know I am suppose to pass the instance of a class to the GetValue method. But how can I pass the current instance (the one that's launching the logic)?
Or: Is there an other way of getting the field's value?
As Lucas says, the problem will be calling ToString() when you shouldn't. Presumably your attribute should only be applied to string fields anyway, so the simplest approach is just to cast the result to string. If that cast fails, it indicates a bigger bug (the attribute being applied incorrectly).
if (string.IsNullOrEmpty((string) field.GetValue(this)))
The GetValue call is fine. The problem lies in the return value on which you're calling ToString.
If GetValue returns null, then ToString will be called on this null value, and this will throw the NullReferenceException.
Do something like this instead:
var value = field.GetValue(this);
if (value == null || string.IsNullOrEmpty(value.ToString()))
{
// write down the name of the field
}

c# .NET dynamic custom control, dynamic return type

Here's what i want to achieve:
We have created our own entityframework, and we pass our entity object onto a custom control. Based on the type (string, boolean, ...) and the rights we want out control to be created on the page different.
For instance:
type=string, access=read => display as label
type=string, access=edit => display as textbox
type=boolean, access=read => display as disabled checkbox
type=boolean, access=edit => display as enabled checkbox
We might add more later but we'll start with these.
I have created a custom control that inherits from the usercontrol class. The control is rendered on the page perfectly, this part i can manager.
The problem however is getting the data from it. Control does not have a text or checked property. So i have to create a Value property of my own.
What i want this property to do is return a Boolean when it's a checkbox, and return Text when it's a label or a textbox.
Is this possible at all and if so how do i do it? (i heard something about reflection, but i'm unsure how to give one getter multiple return type possibilities)
Since everything in C# implicitly inherits from System.Object (even value types), you can make that the return and cast it to the required type later. The is operator can be used to check the type.
object obj = "foo";
bool isString = obj is string; //true
Alternatively, C# has a dynamic keyword which can be used to hold any type.
class Foo
{
public dynamic Value { get; private set; }
public Foo( string value )
{
this.Value = value;
}
public Foo( bool value )
{
this.Value = value;
}
}
This allows you to use operators and call instance members without checking types and casting, but beware, dynamic is resolved by the framework via reflection at runtime, which means any errors won't be caught at compile time and you'll end up getting exceptions.
The following compiles just fine, but will throw at runtime.
Foo BooleanFoo = new Foo( true );
Foo StringFoo = new Foo( "StackOverflow" );
if( BooleanFoo.Value ) //returns true
...
if( StringFoo.Value ) //throws an exception
...
StringFoo.Value.Substring( 0, 5 ); //returns 'Stack'
BooleanFoo.Value.Substring( 0, 5 ); //throws an exception

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);
)

Using reflection with generic types and implicit conversions

I'm trying to use reflection to set properties on some OpenXML types (e.g. Justification). Assigning a value by enumerating all possibilities is straight-forward:
// attr is an XmlAttribute, so .Name and .Value are Strings
if (attr.Name == "Val")
{
if (element is Justification)
{
((Justification)element).Val = (JustificationValues)Enum.Parse(typeof(JustificationValues), attr.Value);
return;
}
else
{
// test for dozens of other types, such as TabStop
}
}
What makes this difficult to do via reflection is:
1) The type of the Val property is EnumValue<T>, so I don't know how to extract the type to pass as the first argument to Enum.Parse.
2) There is an implicit conversion from the actual enumeration type to the EnumValue<> type, which I don't know how to invoke with reflection.
I would like the code to end up looking something like:
PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = ConvertToPropType(pInfo.PropertyType, attr.Value); /* this
would return an instance of EnumValue<JustificationValues> in this case */
pInfo.SetValue(element, value, null);
How do I implement ConvertToPropType? Or is there a better solution?
Thanks
Edit:
I got a solution working using Earwicker's suggestion, but it relies on the convenient fact that the enumeration's type name can be derived from the node's type name ("Justification" -> "JustificationValues"). I'm still curious how to solve this in the general case, though.
Edit2:
GetGenericArguments got me the rest of the way there. Thanks.
If the attribute value is just a string, I assume you already have some way of figuring out that the string identifies a value from a specific enumeration. In your example you have it hard-coded, so I'm not sure if that's what you want or what you want to change.
Assuming you know it's an enumeration, and you know which enumeration, you already know how to get an object containing a boxed value of the right enum type, as in your snippet.
Now if I assume EnumValue<T> has a constructor that takes a T.
Type genericType = typeof(EnumValue<>);
Type concreteType = genericType.MakeGenericType(typeof(JustificationValues));
Now concreteType is the type EnumValue<JustificationValues>.
From that you can get a constructor, hopefully one that takes a JustificationValues parameter, and Invoke it.
Update
Ahh, I see what you're doing now. You use the XML attribute name to pick a C# property. You need to be able to detect whether that property is of a type EnumValue<T>, and find out what T is.
PropertyInfo p = // ... get property info
Type t = p.GetType();
if (t.IsGenericType &&
t.GetGenericTypeDefinition == typeof(EnumValue<>))
{
Type e = t.GetGenericArguments()[0]; // get first (and only) type arg
// e is the enum type...
Give that a try.
.Net 4.0 adds support to do a late bound implict or explict conversion. This is simplified in the open source framework ImpromptuInterface with it's static method called InvokeConvert. In your ideal example it would work like this:
PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = Impromptu.InvokeConvert(attr.Value, pInfo.PropertyType);
pInfo.SetValue(element, value, null);
This might only work with basic types but it was good enough for what I'm doing
PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = System.Convert.ChangeType(attr.Value, pInfo.PropertyType);
pInfo.SetValue(element, value, null);

Categories