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.
Related
I am using reflection to instantiate a class and insert values in its nested properties.
Here's an example of what im trying to do:
https://dotnetfiddle.net/pBgeeV
private static void ApplyValueToProperty(object entityInstance, Type instanceType, string propName, string propValue)
{
// Current only supports an object inside another object.
// Future TODO> GO RECURSIVE TO FIND AND INSTANTIATE ALL NESTED PROPERTIES OR CLASSES!
if (propName.Contains("."))
{
string[] splittedName = propName.Split('.');
// Get type of current property
PropertyInfo currentProp = instanceType.GetProperty(splittedName[0]);
Type nestedPropertyType = currentProp.PropertyType;
// Get current object applied to this current prop
var otherObject = currentProp.GetValue(entityInstance);
if (otherObject == null)
{
// Create instance of nested property class and set its value to a new instance
otherObject = Activator.CreateInstance(nestedPropertyType);
currentProp.SetValue(entityInstance, otherObject);
}
SetSafePropertyValue(otherObject, nestedPropertyType, splittedName[1], propValue);
}
else
{
SetSafePropertyValue(entityInstance, instanceType, propName, propValue);
}
}
First I create the main class outside of this method scope. (Test class).
Then all other propName will have the value of nested properties or classes, example:
I will have a name like "OtherTest.AnotherTest.Name",
If 'Test' has another property of type 'OtherTest', it would create 'OtherTest' and then create 'AnotherTest' and then put value on correct 'Name'.
I would like to change the first if statement to be recursive and instantiate de correct class and insert values to the correct property. In the end I want to have a complete object created with my necessary properties instantiated and values applied.
Note: I want to avoid using GetProperties() because my class has lots of properties from base members, and it consumes a lot of memory.
Instead of this line
SetSafePropertyValue(otherObject, nestedPropertyType, splittedName[1], propValue);
you could do this
ApplyValueToProperty(otherObject, otherObject.GetType(), string.Join(".", splittedName.Skip(1)), propValue);
The idea is to recursivly call ApplyValueToProperty with the first part of the propName dropped hence the Skip(1) in string.Join(".", splittedName.Skip(1)), this will drop the first part again and again till we hit the following condition with false
if (propName.Contains("."))
The else part of this if will stop the recursion.
I have an XML file with classes name like this:
<ActiveMonitorsList>
<MonitorName>CertificatesMonitor</MonitorName>
<MonitorName>ServicesMonitor</MonitorName>
<MonitorName>LogsMonitor</MonitorName>
<MonitorName>DBMonitor</MonitorName>
</ActiveMonitorsList>
Each of this classes containts a method: bool SingleCheck();
I would like to execute this bool SingleCheck() method for each class that is in this XML file.
What is the best way to do this?
This is what I have so far - it doesn't work:
foreach (string monitorName in monitorsList)
{
Type thisType = GetType();
MethodInfo singleMonitorMethod = thisType.GetMethod("{monitorName}.SingleCheck");
bool methodResult = singleMonitorMethod.Invoke(...);
}
In place of (...) - don't know what to put here, but I want to get
the result of the method (it's always bool).
All of those methods I want to pass as paramters are static.
I guess delegates, Actions or Func<> have to go in here...
Thank You very much in advance!
Edit: Each name in XML points to a separate class. Each class have the same named method: public static bool SingleCheck().
What I want to do is:
get all the monitors names (classes names will be the same)
invoke a method (it has the same name in each class) inside EVERY
class present on that list.
EDIT - PROBLEM SOLVED:
When I first created my project, I included separate folder for all monitors. Then I changed my mind, deleted this folder and added manually SAME FILES to my solution. In this way - those files still had "using <namespace>.Monitors"...
And that's why I couldn't list those classes and the Types were still nulls...
Thanks for all suggestions ! ;)
I would suggest to take this overload of the method Invoke It wants an object(calling instance) and a set of input parameters for the method from you.
Since it is a static method, you can calmly pass null as the first parameter and because you method does not have any parameters you again can calmly pass null as the second value. Don't forget to cast object to the corresponding return type. In your case bool.
bool methodResult = (bool)singleMonitorMethod.Invoke(null, null);
To get the correct Type you actually need to know the namespace! So this would look like this:
foreach (string monitorName in monitorsList)
{
string typeName = $"{yourNameSpace}.{monitorName}";
Type thisType = Type.GetType(typeName);
MethodInfo singleMonitorMethod = thisType.GetMethod("SingleCheck");
bool methodResult = (bool)singleMonitorMethod.Invoke(null, null);
}
If the loop is in the same namespace this should also work:
Type thisType = Type.GetType($"{GetType().Namespace}.{monitorName}");
thisType.GetMethod("{monitorName}.SingleCheck") won't work because of two reasons. 1) You forgot the string interpolation $-sign and thus are searching for a method called "{monitorName}.SingleCheck" which obviously can't exist with such a name. 2) Instead of thisType you need to provide the type containing the method.
Invoke needs to be called with the instance as first parameter - null for static methods - and an object array for the method parameters.
Assuming that your monitor classes are in the same assembly like your current type you would need to do the following:
foreach (string monitorName in monitorsList)
{
Type monitorType = GetType().Assembly.GetExportedTypes().Single(x => x.Name == monitorName);
MethodInfo singleMonitorMethod = monitorType.GetMethod("SingleCheck");
bool methodResult = (bool)singleMonitorMethod.Invoke(null, Array.Empty<object>());
}
I prefer Array.Empty over new object[0] or new object[] { } because it doesn't create a new object every time.
Edited: Changed the type discovery according to Mong Zhu's comment that GetType(monitorName) does need the fully-qualified name.
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
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);
)
I have a C# method say:
MyMethod(int num, string name, Color color, MyComplexType complex)
Using reflection, how can I distinctly identify each of the parameter types of any method?
I want to perform some task by parameter type. If the type is simple int, string or boolean then I do something, if it is Color, XMLDocument, etc I do something else and if it is user defined type like MyComplexType or MyCalci etc then I want to do certain task.
I am able to retrieve all the parameters of a method using ParameterInfo and can loop through each parameter and get their types. But how can I identify each data type?
foreach (var parameter in parameters)
{
//identify primitive types??
//identify value types
//identify reference types
}
Edit: this is apart of my code to create a propert grid sort of page where I want to show the parameter list with data types for the selected method. If the parameter has any userdefined type/reference type then I want to expand it further to show all the elements under it with datatypes.
Make use of ParameterInfo.ParameterType
using System;
using System.Reflection;
class parminfo
{
public static void mymethod (
int int1m, out string str2m, ref string str3m)
{
str2m = "in mymethod";
}
public static int Main(string[] args)
{
Console.WriteLine("\nReflection.Parameterinfo");
//Get the ParameterInfo parameter of a function.
//Get the type.
Type Mytype = Type.GetType("parminfo");
//Get and display the method.
MethodBase Mymethodbase = Mytype.GetMethod("mymethod");
Console.Write("\nMymethodbase = " + Mymethodbase);
//Get the ParameterInfo array.
ParameterInfo[]Myarray = Mymethodbase.GetParameters();
//Get and display the ParameterInfo of each parameter.
foreach (ParameterInfo Myparam in Myarray)
{
Console.Write ("\nFor parameter # " + Myparam.Position
+ ", the ParameterType is - " + Myparam.ParameterType);
}
return 0;
}
}
If you need to check the System.Type once retrieved you can use IsPrimitive and IsByRef as mentioned by David. In addition you can also use IsValueType. There are a significant number of Is* properties within the System.Type class. Your best bet would be to check the MSDN documentation on each Is* property ie...IsClass states...
Gets a value indicating whether the
Type is a class; that is, not a value
type or interface.
Therefore one could deduce that IsValueType does not need to be called. Keep in mind that a given type can return true across multiple properties in that IsClass could return true AND IsPassByRef could return true. Perhaps provide logic for the known CLR types since those will not change and you know those ahead of time and then build in the logic for complex types as defined by the user. You could take the approach of building in the logic to do this for the CLR types as well; either way would work.
To get the actual Type of the parameter use the ParameterType on the ParameterInfo value. With that value there are several ways you can use it to identify the type. The easiest is with a direct comparison to a known type
if (parameter.ParameterType == typeof(int)) {
...
}
Or in cases where the type is not available a name match can be used (although this is a bit flakier as refactor operations may miss the string constant and silently break the application)
if (parameter.ParameterType.Name == "TheTypeName") {
...
}