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:)
Related
i want to get a string from user and show him a list of enums member and its value.
for example i have this enum
public enum exampleEnum
{
[MyCustomProperty(customName = "نام1")]
member1 = 1,
[MyCustomProperty(customName = "نام2")]
member2 = 2,
}
help me for create this function
public List<enumResult> GetEnumDetailWithName(string enumName)
{
???
return result;// {name:'member1',value:1,customName='نام1'},{name:'member2',value:2,customName='نام2'}
}
i have write this code but not complete
in this lines get enum from all assembly and its done
then get all members of enum currectly
then for any member of enum add a member to result list , filling name is true but i cant fill Value and CustomName Field ...
public List<enumDetail> GetEnumDetailWithName(string enumName)
{
var enumFullName = $"Bamdad.PublicEnum.Enums+{enumName}";
var assemblyList = AppDomain.CurrentDomain.GetAssemblies();
Type type = null;
foreach (var assembly in assemblyList)
{
type = assembly.GetType(enumFullName);
if (type == null)
continue;
if (type.IsEnum)
break;
}
if (type == null)
return null;
//until this line get enum currectly
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Static).Where(q=> q?.DeclaringType?.Name == enumName).ToList();
var properties = type.GetProperties();
if (!members.Any()) return null;
var result = new List<enumDetail>();
//get members currectly
foreach (var mem in members)
{
var resultItem = new enumDetail()
{
Name = mem.Name, // true
Value = 0, // i cant get
CustomName = "???" // i cant get
};
result.Add(resultItem);
}
return result;
}
please help me
for this question i should use Enum.GetValues and Enum.GetName , for getting custom attribute write a function and use it
public List<enumDetail> GetEnumMemberFromString(string enumName)
{
var enumFullName = $"Bamdad.PublicEnum.Enums+{enumName}";
var assemblyList = AppDomain.CurrentDomain.GetAssemblies();
Type type = null;
foreach (var assembly in assemblyList)
{
type = assembly.GetType(enumFullName);
if (type == null)
continue;
if (type.IsEnum)
break;
}
if (type == null) return null;
var valuesToNames = Enum.GetValues(type)
.Cast<object>()
.ToDictionary(q => Enum.GetName(type, q),q=> (int)q);
var result = valuesToNames.Select(q => new enumDetail()
{
Name = q.Key,
Value = q.Value,
CustomName = type.GetMyCustomProperty(q.Key)?.ToString() ?? q.Key
}).ToList();
return result;
}
this function is for getting custom attribute
public static object GetMyCustomProperty(this Type type,string member = "")
{
if (type == null)
return "";
MemberInfo[] memInfo = type.GetMember(member);
if (memInfo.Length <= 0) return member;
object[] attrs = memInfo[0].GetCustomAttributes(typeof(MyCustomPropertyAttribute), false);
if (!attrs.Any())
return null;
var result = ((MyCustomPropertyAttribute)attrs[0]);
return result.customName;
}
this is a very simple question.
such as this code:
if(o == null)
{
o = new { };
}
PropertyInfo[] p1 = o.GetType().GetProperties();
foreach(PropertyInfo pi in p1)
{}
but like this:
ModelA.ModelB.ModelC.ModelD.ModelE
how to get ModelE's value by reflect ModelA
There is a solution explained here:
using a helper method:
public static class ReflectionHelper
{
public static Object GetPropValue(this Object obj, String propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return obj.GetType().GetProperty(propName).GetValue(obj, null);
}
foreach (String part in nameParts)
{
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
}
then the method can be used like this:
ModelA obj = new ModelA { */....*/ };
obj.GetPropValue("modelB.modelC.modelD.modelE");
please note that you should pass the property names to the function not the class names.
using nested function to do it like following:
var testObj = new
{
nameA = "A",
ModelB = new
{
nameB = "B",
ModelC = new
{
NameC = "C",
}
}
};
var result = ParseProperty(testObj, null, "ModelA");
public Dictionary<string, object> ParseProperty(object o, Dictionary<string, object> result, string preFix = null)
{
result = result ?? new Dictionary<string, object>();
if (o == null) return result;
Type t = o.GetType();
//primitive type or value type or string or nested return
if (t.IsPrimitive || t.IsValueType || t.FullName == "System.String" || t.IsNested) return result;
var proerties = o.GetType().GetProperties();
foreach (var property in proerties)
{
var value = property.GetValue(o);
result.Add($"{preFix}.{property.Name}", value);
//nested call
ParseProperty(value, result, $"{preFix}.{property.Name}");
}
return result;
}
I assume these are all anonymous types because otherwise you could just do GetProperties() on the type of ModelE.
So you basically have to next 5 of your loops like
foreach (PropertyInfo pi1 in o1.GetType().GetProperties())
{
if (pi.Name = "ModelB") // or some other criterion
{
o2 = pi1.GetValue(o1);
foreach (PropertyInfo pi2 in o2.GetType().GetProperties())
{
if (pi.Name = "ModelC") // or some other criterion
{
o3 = pi1.GetValue(o2);
// and so on
}
}
}
}
Following is a class
public class Attribute
{
public string Name { get; set; }
public string Value { get; set; }
}
Following is the code in my main method
{
var test = new List<Attribute>();
test.Add(new Attribute { Name = "Don", Value = "21" });
test.Add(new Attribute { Value = "34", Name = "Karthik" });
var test1 = new List<Attribute>();
test1.Add(new Attribute { Name = "Don", Value = "21" });
test1.Add(new Attribute { Value = "34", Name = "Karthik" });
var obj = new Program();
var areEqual1 = obj.CompareList<List<Attribute>>(test, test1);
}
I have a ComapreList method
public bool CompareList<T>(T firstList, T secondList) where T : class
{
var list1 = firstList as IList<T>;
return true;
}
Now, list1 has null. I know that .net does not allow us to do this. But is there any other way where I can cast this generic list. My purpose is to compare each property value of these two list. I am using reflection to get the property but it works only if I can convert the firstlist/secondlist to something enumerable. if I directly use the name of the class in the IList<> (firstList as IList<Attribute>) it works, but not if I give <T>. Please help.
Just create method parameterized by type of lists items type. Even more, you can create method which compares any type of collections:
public bool CompareSequences<T> (IEnumerable<T> first, IEnumerable<T> second,
Comparer<T> comparer = null)
{
comparer = comparer ?? Comparer<T>.Default;
if (first == null)
throw new ArgumentNullException(nameof(first));
if (second == null)
throw new ArgumentNullException(nameof(second));
var firstIterator = first.GetEnumerator();
var secondIterator = second.GetEnumerator();
while(true)
{
bool firstHasItem = firstIterator.MoveNext();
bool secondHasItem = secondIterator.MoveNext();
if (firstHasItem != secondHasItem)
return false;
if (!firstHasItem && !secondHasItem)
return true;
if (comparer.Compare(firstIterator.Current, secondIterator.Current) != 0)
return false;
}
}
If collection items are primitive types, you can use default comparer. But if collections contain custom items, you need either IComparable to be implemented by collection items type:
public class Attribute : IComparable<Attribute>
{
public string Name { get; set; }
public string Value { get; set; }
public int CompareTo (Attribute other)
{
int result = Name.CompareTo(other.Name);
if (result == 0)
return Value.CompareTo(other.Value);
return result;
}
}
Or you can create and pass comparer instance. You can create comparer which is using reflection to compare fields/properties of some type. But it's not as simple as you might think - properties can be complex type or collections.
Usage:
var areEqual1 = obj.CompareSequences(test, test1);
If you don't need to compare objects with complex structure (which have inner collections and other custom objects) then you can use comparer like this one:
public class SimplePropertiesComparer<T> : Comparer<T>
{
public override int Compare (T x, T y)
{
Type type = typeof(T);
var flags = BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance;
foreach (var property in type.GetProperties(flags))
{
var propertyType = property.PropertyType;
if (!typeof(IComparable).IsAssignableFrom(propertyType))
throw new NotSupportedException($"{propertyType} props are not supported.");
var propertyValueX = (IComparable)property.GetValue(x);
var propertyValueY = (IComparable)property.GetValue(y);
if (propertyValueX == null && propertyValueY == null)
continue;
if (propertyValueX == null)
return -1;
int result = propertyValueX.CompareTo(property.GetValue(y));
if (result == 0)
continue;
return result;
}
return 0;
}
}
And pass it to sequence comparer
var equal = obj.CompareSequences(test, test1, new SimplePropertiesComparer<Attribute>());
Change the signature of your method and remove the then redundant cast:
public bool CompareList<T>(IList<T> firstList, IList<T> secondList) where T : class
{
var list1 = firstList as IList<T>; // Cast is not necessary any more
return true;
}
public bool CompareGenericLists<T, U>(List<T> list1, List<U> list2)
{
try
{
if (typeof(T).Equals(typeof(U)))
{
//For checking null lists
if (list1 == null && list2 == null)
return true;
if (list1 == null || list2 == null)
throw new Exception("One of the Lists is Null");
if (list1.Count.Equals(list2.Count))
{
Type type = typeof(T);
//For primitive lists
if (type.IsPrimitive)
{
int flag = 0;
for (int i = 0; i < list1.Count; i++)
{
if (list1.ElementAt(i).Equals(list2.ElementAt(i)))
flag++;
}
if (flag != list1.Count)
throw new Exception("Objects values are not same");
}
//For Reference List
else
{
for (int i = 0; i < list1.Count; i++)
{
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
Object1Value = type.GetProperty(property.Name).GetValue(list1.ElementAt(i)).ToString();
Object2Value = type.GetProperty(property.Name).GetValue(list2.ElementAt(i)).ToString();
if (Object1Value != Object2Value)
{
throw new Exception("Objects values are not same");
}
}
}
}
}
else
throw new Exception("Length of lists is not Same");
}
else
throw new Exception("Different type of lists");
}
catch(Exception ex)
{
throw ex;
}
return true;
}
this method can be used for both primitive and reference lists.try this method.It will compare type,counts,and members of lists.
I'm trying to build a generic method to convert objects into ExpandoObjects and I can handle all cases except when one of the properties is an array.
public static ExpandoObject ToExpando(this object AnonymousObject) {
dynamic NewExpando = new ExpandoObject();
foreach (var Property in AnonymousObject.GetType().GetProperties()) {
dynamic Value;
if (IsPrimitive(Property.PropertyType)) {
Value = Property.GetValue(AnonymousObject);
} else if (Property.PropertyType.IsArray) {
dynamic ArrayProperty = new List<dynamic>();
var ArrayElements = (Array)Property.GetValue(AnonymousObject);
for (var i = 0; i < ArrayElements.Length; i++) {
var Element = ArrayElements.GetValue(i);
if (IsPrimitive(Element.GetType())) {
ArrayProperty.Add(Element);
} else {
ArrayProperty.Add(ToExpando(Element));
}
}
Value = ArrayProperty;//.ToArray();
} else {
Value = ToExpando(Property.GetValue(AnonymousObject));
}
((IDictionary<string, object>) NewExpando)[Property.Name] = Value;
}
return NewExpando;
}
private static bool IsPrimitive(System.Type type) {
while (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>)) {
// nullable type, check if the nested type is simple.
type = type.GetGenericArguments()[0];
}
return type.IsPrimitive || type.IsEnum || type.Equals(typeof (string)) || type.Equals(typeof (decimal));
}
Any property that's an array doesn't seem to be a dynamic object and when I use it on something like a razor template the array elements and properties aren't visible.
For example, if I do this:
var EmailParams = new {
Parent = new {
Username = "User1",
},
Students = new [] {new {Username = "Student1", Password = "Pass1"} }
};
I get the following:
As you can see the anonymous object at the top has an array of Students, but the converted ExpandoObject does not.
Does anyone have any insight on how I would change the code to add support for arrays/list in the ExpandoObject?
Thanks!
When you create an object like
var person = new
{
FirstName = "Test",
LastName = new List<Person>() { new Person()
{
FirstName = "Tes2"
} }
};
LastName is a generic list and Property.PropertyType.IsArray returns false on that case. So your "array/list" is not treated with this logic that you are trying to add
dynamic ArrayProperty = new List<dynamic>();
var ArrayElements = (Array)Property.GetValue(AnonymousObject);
for (var i = 0; i < ArrayElements.Length; i++) {
var Element = ArrayElements.GetValue(i);
if (IsPrimitive(Element.GetType())) {
ArrayProperty.Add(Element);
} else {
ArrayProperty.Add(ToExpando(Element));
}
}
Hope this helps
Just one remark, you don't need to check again inside the logic of the if(Property.Property.Type.IsArray) the Primitive values, you did it, that is one of the stop conditions of your recursion. Below is the same code with the difference that I am mentioning
public static ExpandoObject ToExpando(this object AnonymousObject)
{
dynamic NewExpando = new ExpandoObject();
foreach (var Property in AnonymousObject.GetType().GetProperties())
{
dynamic Value;
if (IsPrimitive(Property.PropertyType))
{
Value = Property.GetValue(AnonymousObject);
}
else if (Property.PropertyType.IsArray)
{
var ArrayProperty = new List<ExpandoObject>();
var elements = Property.GetValue(AnonymousObject) as IEnumerable;
//is the same as foreach all elements calling to Expando and adding them to elemenstArray
if (elements != null)
ArrayProperty.AddRange(from object elem in elements select ToExpando(elem));
Value = ArrayProperty;
}
else
{
Value = ToExpando(Property.GetValue(AnonymousObject));
}
((IDictionary<string, object>)NewExpando)[Property.Name] = Value;
}
return NewExpando;
}
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 !