How to supply parameters to constructors, not known in advance? - c#

This function will load an assembly, let the user select a form from a list, and then try to invoke it. If successful, returning the form.
My problem is how to instantiate the constructor with parameters that is of the expected type.
if the constructor expect List<string> an empty List<String> should be supplied, not just null.
Any Ideas?
private Form SelectForm(string fileName)
{
Assembly assembly = Assembly.LoadFrom(fileName);
var asmTypes = assembly.GetTypes().Where(F => F.IsSubclassOf(typeof(Form)));
string SelectedFormName;
using (FrmSelectForm form = new FrmSelectForm())
{
form.DataSource = (from row in asmTypes
select new { row.Name, row.Namespace, row.BaseType }).ToList();
if (form.ShowDialog(this) != DialogResult.OK)
return null;
SelectedFormName = form.SelectedForm;
}
Type t = asmTypes.Single<Type>(F => F.Name == SelectedFormName);
foreach (var ctor in t.GetConstructors())
{
try
{
object[] parameters = new object[ctor.GetParameters().Length];
for (int i = 0; i < ctor.GetParameters().Length; i++)
{
parameters[i] = ctor.GetParameters()[i].DefaultValue;
}
return Activator.CreateInstance(t, parameters) as Form;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
return null;
}

If you know what is a parameter type, replace:
parameters[i] = ctor.GetParameters()[i].DefaultValue;
to
parameters[i] = new List<string>();
If you don't know, you need create instance using same reflection methods:
object p1 = Activator.CreateInstance(parameters[i].ParameterType),
return Activator.CreateInstance(t, [p1]) as Form;

in order to create objects from types definitions this method works very well.
private Form SelectForm(string fileName,string formName)
{
Assembly assembly = Assembly.LoadFrom(fileName);
var asmTypes = assembly.GetTypes().Where(F => F.IsSubclassOf(typeof(Form)));
Type t = asmTypes.Single<Type>(F => F.Name == formName);
try
{
var ctor = t.GetConstructors()[0];
List<object> parameters = new List<object>();
foreach (var param in ctor.GetParameters())
{
parameters.Add(GetNewObject(param.ParameterType));
}
return ctor.Invoke(parameters.ToArray()) as Form;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return null;
}
...
public static object GetNewObject(Type t)
{
try
{
return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
}
catch
{
return null;
}
}

Related

C# - Cast to list takes up 4.5 sec - only 76 items in collection

I've got a collection (languages). In this collection are 76 items. I want to cast this collection to a list with languages.Cast<LanguageTextValue>().ToList();. Actually it takes around 4.5 sec. to take this step. Can anyone tell me, how I can have a further look, why this steps takes so long or show me a method to cast this collection to a list in a quicker way?
LanguageTextValueCollection languages = new LanguageTextValueCollection(LanguageTextValueKind.LanguageNo, new object[] { languageNo });
var languageList = languages.Cast<LanguageTextValue>().ToList();
The data of this collection is stored in a DataBuffer. So loading the data from the database is not the reason why it takes so long. It is really the step casting the collection to a list.
This is the code of the collection:
using System;
using System.Collections;
using System.ComponentModel;
namespace Library.Languages
{
#region Enums
public enum LanguageTextValueKind
{
All,
LanguageNo
}
#endregion Enums
public class LanguageTextValueCollection : CommonObjectCollection
{
#region DataBuffer
public static System.Data.DataTable DataBuffer = new LanguageTextValueCollection(LanguageTextValueKind.All, null).DataSource;
#endregion DataBuffer
#region Properties
public override Library.CommonObject this[int Index]
{
get
{
return new LanguageTextValue(new object[] { this.DataSource.Rows[Index]["LanguageNo"].ToString(), this.DataSource.Rows[Index]["LanguageTextTypeID"].ToString() });
}
}
public new LanguageTextValue this[string SearchExpression]
{
get
{
System.Data.DataRow[] rows = base[SearchExpression];
if (rows.Length > 0)
{
LanguageTextValue temp = new LanguageTextValue(new object[] { rows[0]["LanguageNo"].ToString(), rows[0]["LanguageTextTypeID"].ToString() });
if (temp.RequestResult.State == ResultState.Success)
return temp;
else
return null;
}
else
{
rows = LanguageTextValueCollection.DataBuffer.Select(SearchExpression + " AND LanguageNo=9");
if (rows.Length > 0)
{
LanguageTextValue temp = new LanguageTextValue(new object[] { rows[0]["LanguageNo"].ToString(), rows[0]["LanguageTextTypeID"].ToString() });
if (temp.RequestResult.State == ResultState.Success)
return temp;
else
return null;
}
else
return null;
}
}
}
#endregion Properties
#region Constructors
public LanguageTextValueCollection()
{
}
public LanguageTextValueCollection(LanguageTextValueKind KindOfObject, object[] Value)
{
this.DataGet(KindOfObject, Value);
}
#endregion Constructors
#region Data Methods
public Result DataGet(LanguageTextValueKind kindOfObject, object[] value)
{
try
{
if (LanguageTextValueCollection.DataBuffer == null)
{
this.FillParameters(System.Data.CommandType.StoredProcedure, "sp_tbl_LanguageText_Value_Get_By_All", null, null);
this.DBDataGet(this.CommandObject);
if (this.RequestResult.State == ResultState.Failure)
throw new Exception(this.RequestResult.Message);
LanguageTextValueCollection.DataBuffer = this.DataSource.Copy();
}
this.DataSource = LanguageTextValueCollection.DataBuffer.Clone();
switch (kindOfObject)
{
case LanguageTextValueKind.All:
this.DataSource = LanguageTextValueCollection.DataBuffer.Copy();
break;
case LanguageTextValueKind.LanguageNo:
System.Data.DataRow[] rows = LanguageTextValueCollection.DataBuffer.Select("LanguageNo = " + value[0].ToString());
foreach (System.Data.DataRow row in rows)
this.DataSource.Rows.Add(row.ItemArray);
break;
}
if (this.RequestResult.State == ResultState.Success)
this.Count = this.DataSource.Rows.Count;
}
catch (Exception ex)
{
this.RequestResult = new Result(ex);
}
return this.RequestResult;
}
protected new Result FillParameters(System.Data.CommandType CommandType, string CommandText, string[] ColumnName, object[] Parameter)
{
try
{
this.CommandObject = new System.Data.SqlClient.SqlCommand(CommandText);
this.CommandObject.CommandType = CommandType;
if (Parameter != null)
for (int i = 0; i < ColumnName.Length; i++)
this.CommandObject.Parameters.Add(new System.Data.SqlClient.SqlParameter("#" + ColumnName[i], Parameter[i]));
}
catch (Exception ex)
{
this.RequestResult = new Result(ex);
}
return this.RequestResult;
}
#endregion Data Methods
}
}

how do I convert Type Object to a List in C# with reflection. I can access the properties in the object but cannot access the values, any suggestions?

I want to be able to loop through a generic obj of type object and display the value of each property.
I have googled but can't find a way to access the value of each Object in a object array.
This is a test application to make sure an API call returns stuff, but i want to display the data in the UI
Current code:
[Route("Home/Index/")]
[HttpPost]
public string Index(string strv_Params, string strv_Method)
{
try
{
#region Region Create a new instance of the assebly
//Declare the assembly
Assembly executingAssebbly = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.FullName.Contains("DLL_Example")).FirstOrDefault();
Type customerType = executingAssebbly.GetType("CLASS_EXAMPLE");
//Assebly calls the new constructor
object customerInstance = Activator.CreateInstance(customerType);
//I need to call a mathod that is used like a construcor to set some globle varables
MethodInfo setStartupProperties = customerType.GetMethod("METHOD_NAME_EXAMPLE");
//Params needed in the construtor
object[] PropertySettings = new object[3];
PropertySettings[0] = "PropertySettings";
PropertySettings[1] = "PropertySettings";
PropertySettings[2] = "PropertySettings";
//Call the Constructor to set up the assebly
setStartupProperties.Invoke(customerInstance, PropertySettings);
#endregion
//Build up a Property array from the UI
#region Region Buiild My Params
List<string> thesplit = new List<string>();
foreach (var item in strv_Params.Split(','))
{
var ahh = item.Split('|');
thesplit.Add(ahh[1]);
}
int count = thesplit.Count();
object[] paramters = new object[count];
int li = 0;
foreach (var item in thesplit)
{
if (item == "Ref")
{
paramters[li] = "";
}
else
{
paramters[li] = item;
}
li++;
}
#endregion
//Declare the Method info using the string passed from the UI
MethodInfo GetFullNameMathod = customerType.GetMethod(strv_Method);
//Call the method using reflection with the params passing in from the UI
object retur = GetFullNameMathod.Invoke(customerInstance, paramters);
//Converts object to list of objects
object[] arr_obj = (object[])retur;
IEnumerable<object> lstl_OBJ = arr_obj.ToList();
string htmlReturn = "";
foreach (object objl_THIS in lstl_OBJ)
{
//here I want to access each value in objl_THIS
}
return htmlReturn;
}
catch (Exception ex)
{
return ex.Message;
}
}
}
If you have flat objects you could do it like this:
foreach (object objl_THIS in lstl_OBJ)
{
var type = objl_THIS.GetType();
var propertyInfos = type.GetProperties();
foreach(var propertyInfo in propertyInfos)
{
string name = propertyInfo.Name;
object value = propertyInfo.GetValue(objl_THIS); // <-- value
}
}

Can a DynamicParameter rely on the values of other DynamicParameters?

I have a GetDynamicParameters() on cmdlet Get-DateSlain that does something like this:
public object GetDynamicParameters()
{
List<string> houseList = {"Stark", "Lannister", "Tully"};
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a house name",
},
new ValidateSetAttribute(houseList.ToArray()),
};
if (!this.ContainsKey("House"))
{
this.runtimeParameters.Add("House", new RuntimeDefinedParameter("House", typeof(string), attributes));
}
}
And this works as expected - users can type Get-DateSlain -House, and tab through the available houses. However, once a house is chosen, I want to be able to narrow down the results to characters in that house. Furthermore, if it's house 'Stark', I want to allow a -Wolf parameter. So to implement (some value validity checks removed for brevity):
public object GetDynamicParameters()
{
if (this.runtimeParameters.ContainsKey("House"))
{
// We already have this key - no need to re-add. However, now we can add other parameters
var house = this.runtimeParameters["House"].Value.ToString();
if (house == "Stark")
{
List<string> characters = { "Ned", "Arya", "Rob" };
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a character name",
},
new ValidateSetAttribute(characters.ToArray()),
};
this.runtimeParameters.Add("Character", new RuntimeDefinedParameter("Character", typeof(string), attributes));
List<string> wolves = { "Shaggydog", "Snow", "Lady" };
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a wolf name",
},
new ValidateSetAttribute(wolves.ToArray()),
};
this.runtimeParameters.Add("Wolf", new RuntimeDefinedParameter("Wolf", typeof(string), attributes));
}
else if (house == "Lannister")
{
List<string> characters = { "Jaimie", "Cersei", "Tywin" };
// ...
}
// ...
return this.runtimeParameters;
}
List<string> houseList = {"Stark", "Lannister", "Tully"};
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a house name",
},
new ValidateSetAttribute(houseList.ToArray()),
};
this.runtimeParameters.Add("House", new RuntimeDefinedParameter("House", typeof(string), attributes));
}
This looks like it should work, but it doesn't. The GetDynamicParameters function is only called once, and that is before a value is supplied to this.runtimeParameters["House"]. Since it doesn't re-evaluate after that value is filled in, the additional field(s) are never added, and any logic in ProcessRecord that relies on these fields will fail.
So - is there a way to have multiple dynamic parameters that rely on each other?
Have a look a the aswer to this question, it shows a way to access the values of other dynamic parameters in the GetDynamicParameters method:
Powershell module: Dynamic mandatory hierarchical parameters
I adapted the code from the mentioned answer so it can handle SwitchParameters and the raw input parameter is converted to the actual type of the cmdlet parameter. It does not work if the dynamic parameter you want to get the value for is passed via pipeline. I think that is not possible because dynamic parameters are always created before pipeline input is evaluated. Here it is:
public static class DynamicParameterExtension
{
public static T GetUnboundValue<T>(this PSCmdlet cmdlet, string paramName, int unnamedPosition = -1))
{
var context = TryGetProperty(cmdlet, "Context");
var processor = TryGetProperty(context, "CurrentCommandProcessor");
var parameterBinder = TryGetProperty(processor, "CmdletParameterBinderController");
var args = TryGetProperty(parameterBinder, "UnboundArguments") as System.Collections.IEnumerable;
if (args != null)
{
var isSwitch = typeof(SwitchParameter) == typeof(T);
var currentParameterName = string.Empty;
object unnamedValue = null;
var i = 0;
foreach (var arg in args)
{
var isParameterName = TryGetProperty(arg, "ParameterNameSpecified");
if (isParameterName != null && true.Equals(isParameterName))
{
var parameterName = TryGetProperty(arg, "ParameterName") as string;
currentParameterName = parameterName;
if (isSwitch && string.Equals(currentParameterName, paramName, StringComparison.OrdinalIgnoreCase))
{
return (T)(object)new SwitchParameter(true);
}
continue;
}
var parameterValue = TryGetProperty(arg, "ArgumentValue");
if (currentParameterName != string.Empty)
{
if (string.Equals(currentParameterName, paramName, StringComparison.OrdinalIgnoreCase))
{
return ConvertParameter<T>(parameterValue);
}
}
else if (i++ == unnamedPosition)
{
unnamedValue = parameterValue;
}
currentParameterName = string.Empty;
}
if (unnamedValue != null)
{
return ConvertParameter<T>(unnamedValue);
}
}
return default(T);
}
static T ConvertParameter<T>(this object value)
{
if (value == null || Equals(value, default(T)))
{
return default(T);
}
var psObject = value as PSObject;
if (psObject != null)
{
return psObject.BaseObject.ConvertParameter<T>();
}
if (value is T)
{
return (T)value;
}
var constructorInfo = typeof(T).GetConstructor(new[] { value.GetType() });
if (constructorInfo != null)
{
return (T)constructorInfo.Invoke(new[] { value });
}
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception)
{
return default(T);
}
}
static object TryGetProperty(object instance, string fieldName)
{
if (instance == null || string.IsNullOrEmpty(fieldName))
{
return null;
}
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
var propertyInfo = instance.GetType().GetProperty(fieldName, bindingFlags);
try
{
if (propertyInfo != null)
{
return propertyInfo.GetValue(instance, null);
}
var fieldInfo = instance.GetType().GetField(fieldName, bindingFlags);
return fieldInfo?.GetValue(instance);
}
catch (Exception)
{
return null;
}
}
}
So for your example you should be able to use it like:
public object GetDynamicParameters()
{
var houseList = new List<string> { "Stark", "Lannister", "Tully" };
var attributes = new Collection<Attribute>
{
new ParameterAttribute { HelpMessage = "Enter a house name" },
new ValidateSetAttribute(houseList.ToArray()),
};
var runtimeParameters = new RuntimeDefinedParameterDictionary
{
{"House", new RuntimeDefinedParameter("House", typeof (string), attributes)}
};
var selectedHouse = this.GetUnboundValue<string>("House");
//... add parameters dependant on value of selectedHouse
return runtimeParameters;
}
After all I'm not sure if it's a good idea trying to get those dynamic parameter values in the first place. It is obviously not supported by PowerShell Cmdlet API (see all the reflection to access private members in the GetUnboundValue method), you have to reimplement the PowerShell parameter conversion magic (see ConvertParameter, I'm sure I missed some cases there) and there is the restriction with pipelined values. Usage at your own risk:)

Get indexed value from object over reflection

I have the problem, that i don't know how to call the this property of an object instance over reflection.
I have the following method:
public object GetValue(SOP sop, object value)
{
// 1. find the this getter/setter method for the value parameter
// 2. Invoke the method and pass the result of GetIndexArray(sop) as a parameter
// 3. return the result of the method
}
which should call the this property of the value-instance and return the value the property returns.
The problem is, that i don't know the type of the value-instance, which is passed as a parameter.
The index which should be passed to the this property is given over the following method:
private object[] GetIndexArray(SOP sop)
But i don't know how to call the this-property. The object-instance can be anything, a string, Dictionary, ...
Does any one has an idea, how to solve the problem?
EDIT:
The GetValue method should do the follwing task, but dynamically over reflection:
public object GetValue(SOP sop, object value)
{
// Non relfection, lets say 'value' is a Dicitionary<string, string>
// return value["key"];
// The type of 'value' is unknown, and the index-parameter (the keys) are accessable over GetIndexArray()
}
EDIT2:
In c# every getter and setter can be invoked via reflection as a method. Is there a way, to get the methods for the "this" property? If it's possible, the problem can be solved with invoking the mesthods.
Call of the GetValue method
object pInst = parentInstance.GetType().GetProperty(VariableName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(parentInstance, null);
var indexChldren = FindChildrenOfType<IndexNode>();
if (indexChldren.Count > 0)
{
pInst = indexChldren[0].GetValue(sop, pInst);
}
I decided to remake the post, since my previous one didn't correctly do what it was supposed to do.
To check which indexers are on an object, you can use the following, which will
Return null when object is null
Return an empty IList when no indexers are found
Return a list of MethodInfos with the found indexers
The trick to check it, is to check if a property has any GetIndexParameters();
public IList<MethodInfo> GetIndexProperties(object obj)
{
if (obj == null)
{
return null;
}
var type = obj.GetType();
IList<MethodInfo> results = new List<MethodInfo>();
try
{
var props = type.GetProperties(System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance);
if (props != null)
{
foreach (var prop in props)
{
var indexParameters = prop.GetIndexParameters();
if (indexParameters == null || indexParameters.Length == 0)
{
continue;
}
var getMethod = prop.GetGetMethod();
if (getMethod == null)
{
continue;
}
results.Add(getMethod);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return results;
}
This works with a List, Dictionary, string, object with indexers, indexers defined with IndexerNameAttribute
As an example, i used this MainMethod, to try several possibilities
object[] exampleHayStack = new object[] {
"This is a test of an indexer",
new TestIndexer(),
null,
string.Empty,
new ClassIndexer(),
new Dictionary<string, string>() { { "key", "value" } },
new List<string>() { "A", "B", "C", "D", "E", "F", "G" } };
ClassIndexer myIndexer = new ClassIndexer();
foreach (var obj in exampleHayStack)
{
var methods = myIndexer.GetIndexProperties(obj);
if (methods == null || methods.Count == 0)
{
Console.WriteLine("{0} doesn't have any indexers", obj);
continue;
}
Console.WriteLine("Testing {0}", obj);
foreach (MethodInfo mi in methods)
{
IList<object> indexParams = new List<object>();
var requiredParams = mi.GetParameters();
foreach (var par in requiredParams)
{
indexParams.Add(myIndexer.ParamForObject(obj, par));
}
try
{
var result = mi.Invoke(obj, indexParams.ToArray());
Console.WriteLine("Result of requesting ({0}) = {1}", string.Join(",", indexParams), result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadLine();
Which then results into:
Testing This is a test of an indexer
Result of requesting (21) = i
Testing TestReflection.Program+TestIndexer
Result of requesting (53) = 5
Result of requesting (Key) = Key item
doesn't have any indexers
Testing
Exception has been thrown by the target of an invocation.
TestReflection.Program+ClassIndexer doesn't have any indexers
Testing System.Collections.Generic.Dictionary`2[System.String,System.String]
Result of requesting (key) = value
Testing System.Collections.Generic.List`1[System.String]
Result of requesting (5) = F
The complete implementation of the ClassIndexer you can find here, it contains an extra method for getting possible key values (but those you have already)
public class ClassIndexer
{
Random pNext = new Random();
public IList<MethodInfo> GetIndexProperties(object obj)
{
if (obj == null)
{
return null;
}
var type = obj.GetType();
IList<MethodInfo> results = new List<MethodInfo>();
try
{
var props = type.GetProperties(System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance);
if (props != null)
{
foreach (var prop in props)
{
var indexParameters = prop.GetIndexParameters();
if (indexParameters == null || indexParameters.Length == 0)
{
continue;
}
var getMethod = prop.GetGetMethod();
if (getMethod == null)
{
continue;
}
results.Add(getMethod);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return results;
}
public object ParamForObject(object obj, ParameterInfo pi)
{
if (obj is IDictionary)
{
int maxNumber = ((IDictionary)obj).Keys.Count;
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next(maxNumber);
}
if (pi.ParameterType.Equals(typeof(string)))
{
int target = pNext.Next(maxNumber);
foreach (var key in ((IDictionary)obj).Keys)
{
target--;
if (target <= 0)
{
return key;
}
}
return null;
}
}
if (obj is string)
{
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next((obj as string).Length);
}
}
if (obj is IList)
{
return pNext.Next(((IList)obj).Count);
}
if (pi.ParameterType.Equals(typeof(string)))
{
return "Key";
}
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next(100);
}
return null;
}
public ClassIndexer()
{
}
}
For the rest, this was a good research, thanks for the question !

Generic Return Type

I would like to write a class which provides me decrypted values for any given domain model. The following logic works fine, but don't know what to declare in the place of XXXXXX.
Sample Domain Model:
public class emp
{
public string empname{get;set;}
}
I will call the DecryptByObject method something like:
var x = DecryptByObject(emp,KEY);
DecryptByObject Method:
public XXXXX DecryptByObject(XXXXXX myObject, string decryptKey)
{
Type t = myObject.GetType();
foreach (MemberInfo mi in t.GetMembers())
{
try
{
if (mi.MemberType == MemberTypes.Property)
{
string value = ((PropertyInfo)mi).GetValue(myObject).ToString();
var bytes = Convert.FromBase64String(value);
var decryValue = MachineKey.Unprotect(bytes, decryptKey);
((PropertyInfo)mi).SetValue(myObject, Encoding.UTF8.GetString(decryValue));
}
}
catch (Exception ex) { }
}
return myObject;
}
You should use like as below.
public T DecryptByObject<T>(T myObject, string decryptKey)
{
Type t = myObject.GetType();
PropertyInfo prop = t.GetProperty("Items");
object list = prop.GetValue(myObject);
foreach (MemberInfo mi in t.GetMembers())
{
try
{
if (mi.MemberType == MemberTypes.Property)
{
string value = ((PropertyInfo)mi).GetValue(myObject).ToString();
var bytes = Convert.FromBase64String(value);
var decryValue = MachineKey.Unprotect(bytes, decryptKey);
((FieldInfo)mi).SetValue(myObject, Encoding.UTF8.GetString(decryValue));
}
}
catch (Exception ex) { }
}
return myObject;
}
Try this:
public T DecryptByObject<T>(T myObject, string decryptKey)
As a side point, you want to add some error checking here as prop could well be null or an error may be thrown:
Type t = myObject.GetType();
PropertyInfo prop = t.GetProperty("Items");
if (prop == null)
{
// handle this error...
}
object list = prop.GetValue(myObject);

Categories