c# Reflection Generic List Count - c#

Is there a cleaner way to get a list count using reflection then this?
Boolean include = false;
foreach (PropertyInfo item in props)
{
var pt = item.PropertyType;
String listType = pt.GetGenericArguments()[0].Name;
// Is there a better solution than this?
switch (listType)
{
case "jsonResult":
var list = v as List<jsonResult>;
include = list.count > 0;
break;
}
}
)
I've tried a variety of ideas from Googling but haven't had any luck.

I didn't completely understand what is the "v" variable, but if it is an object and when it's a collection you want to get its count, you can do that this way:
var count = GetCount(v);
if (!count.HasValue)
continue; // Or any other code here
include = count.Value > 0;
The "GetCount" method:
private static int? GetCount(object #object)
{
var collection = #object as System.Collections.ICollection;
if (collection == null)
return null;
return collection.Count;
}

Related

How to iterate a collection between two items?

Consider for example a List of objects:
List<MyClass> myList;
I have a method which passes two references to items within the list. I want to iterate all items within the given ones (note: I don't know which of the two items comes first within the list):
privat void MyFunction(MyClass listItem, MyClass anotherListItem)
{
foreach(var item in ????)
{
// do something
}
}
Currently I have solved this usecase as follows:
int listItemIdx = myList.IndexOf(listItem);
int anotherListItemIdx = myList.IndexOf(anotherListItem);
if(listItemIdx < anotherListItemIdx )
{
for(int i = listItemIdx ; i <= anotherListItemIdx ; i++)
{
// do stuff
}
}
else
{
for (int i = anotherListItemIdx ; i < listItemIdx ; i++)
{
// do stuff
}
}
I was wondering if there is a more elegant, efficient or built-in solution to this problem?
If you are looking for performance (IndexOf twice can be a bit slow) and generalization
(when myList is not necessary List<T> but IEnumerable<T> only) you can put it as
bool proceed = false;
MyClass lastItem = default;
foreach (var item in myList) {
if (!proceed) {
if (proceed = item == listItem)
lastItem = anotherListItem;
else if (proceed = item == anotherListItem)
lastItem = listItem;
}
if (proceed) {
//TODO: do staff here
if (item == lastItem)
break;
}
}
You iterate three times over the list: Two times in IndexOf, and then once again in your loop. You can make your code more efficient with this code, which iterates only once over the list.
privat void MyFunction(MyClass listItem, MyClass anotherListItem)
{
bool betweenTwoItems = false;
foreach(var item in myList)
{
if(item == listItem || item == anotherListItem)
{
betweenTwoItems = !betweenTwoItems;
if(!betweenTwoItems)
{
break;
}
}
if(betweenTwoItems )
{
// do stuff
}
}
}
We set a bool variable if we are between the two items. In the beginning, it is false. The we iterate over the list and check whether the current item is one of the two method parameters. If this is the case, we invert the value of the bool. If after the inversion of the bool the value is false, we can leave the list. After that, we check whether the bool is true. If so, we can do stuff.
Online demo: https://dotnetfiddle.net/xYcr7V
More generic version of the same idea. So this can be created as extension method for IEnumerable<,>
public static IEnumerable<T> RangeOf<T>(this IEnumerable<T> elements, T el1, T el2,
IEqualityComparer<T> comparer = null)
{
comparer ??= EqualityComparer<T>.Default;
var hasStarted = false;
var end = default;
foreach (T el in elements)
{
if (!hasStarted)
{
hasStarted = comparer.Equals(el, el1) || comparer.Equals(el, el2);
end = comparer.Equals(el, el1) ? el2 : el1;
}
if (hasStarted)
yield return el;
if (comparer.Equals(el, end))
yield break;
}
}
and version with the while loop supporting ranges from el to el. For example for [5, 0, 1, 2, 0, 6] the range [0, 0] will be [0, 1, 2, 0]:
public static IEnumerable<T> RangeOf<T>(this IEnumerable<T> elements, T el1, T el2,
IEqualityComparer<T> comparer = null)
{
comparer ??= EqualityComparer<T>.Default;
var hasStarted = false;
var end = default;
var it = elements.GetEnumerator();
while (!hasStarted && it.MoveNext())
{
T el = it.Current;
hasStarted = comparer.Equals(el , el1) || comparer.Equals(el , el2);
end = comparer.Equals(it.Current, el1) ? el2 : el1;
}
if (hasStarted)
yield return it.Current;
while (it.MoveNext())
{
yield return it.Current;
if (comparer.Equals(it.Current, end))
yield break;
}
}
both can be used like this
foreach (var el in list.RangeOf(listItem, anotherListItem))
// Do with el whatever you want to do
Is the list sorted? The you can use that fact to realize which item must be first. Nevertheless you can do with one for-loop, if you prefer:
private static void MyFunction(string item1, string item2)
{
List<string> input = new() {"A", "B", "C", "D", "E"};
int index1 = input.IndexOf(item1);
int index2 = input.IndexOf(item2);
int beginIndex = Math.Min(index1, index2);
int count = Math.Abs(index1 - index2) + 1;
foreach (string item in input.GetRange(beginIndex, count))
{
Console.Write(item);
}
}
Your existing solution can be improved:
int listItemIdx = myList.IndexOf(listItem);
int anotherListItemIdx = myList.IndexOf(anotherListItem);
int startIdx = Math.Min(listItemIdx, anotherListItemIdx);
int endIdx = Math.Max(listItemIdx, anotherListItemIdx);
for(int i = startIdx ; i <= endIdx ; i++)
{
// do stuff
}
Thus, the code duplication disappears and only a minor refactoring is required.
To create a range-loop version, you can create a subset using GetRange(), something like:
int listItemIdx = myList.IndexOf(listItem);
int anotherListItemIdx = myList.IndexOf(anotherListItem);
int startIdx = Math.Min(listItemIdx, anotherListItemIdx);
int endIdx = Math.Max(listItemIdx, anotherListItemIdx);
var subset = myList.GetRange(startIdx, endIdx - startIdx);
foreach(var item in subset)
{
// do stuff
}
Thus, filtering the list and processing the list can now be separated.

Get value of property with reflection in C#

i need to get value of property in C# with reflection .
i need to find lenght of string and compare to max .
i write this code :
public static bool ValidateWithReflection(T model)
{
bool validate = false;
var cls = typeof(T);
PropertyInfo[] propertyInfos = cls.GetProperties();
foreach (PropertyInfo item in propertyInfos)
{
var max = item.GetCustomAttributes<MaxLenghtName>().Select(x => x.Max).FirstOrDefault();
if (max != 0)
{
var lenght = item.GetType().GetProperty(item.Name).GetValue(cls, null);
if ((int)lenght > max)
{
return validate = true;
}
}
}
return validate;
}
and this for get value of property :
var lenght = item.GetType().GetProperty(item.Name).GetValue(cls, null);
but it show me this error :
Message "Object does not match target type." string
now whats the problem ? how can i solve this problem ?
What is item.GetType().GetProperty(item.Name) supposed to do? item is a PropertyInfo instance. You're not looking to get properties of that, but of your model.
So simplify your code to this:
var value = item.GetValue(model) as string;
if (value?.Length > max)
{
return validate = true;
}

Finding all identifiers containing part of the token

I know I can get a string from resources using
Resources.GetIdentifier(token, "string", ctx.ApplicationContext.PackageName)
(sorry, this is in C#, it's part of a Xamarin.Android project).
I know that if my elements are called foo_1, foo_2, foo_3, then I can iterate and grab the strings using something like
var myList = new List<string>();
for(var i = 0; i < 4; ++i)
{
var id = AppContent.GetIdentifier(token + i.ToString(), "string", "package_name");
if (id != 0)
myList.Add(AppContext.GetString(id));
}
My issue is that my token names all begin with "posn." (the posn can denote the position of anything, so you can have "posn.left_arm" and "posn.brokenose"). I want to be able to add to the list of posn elements, so I can't really store a list of the parts after the period. I can't use a string-array for this either (specific reason means I can't do this).
Is there a way that I can use something akin to "posn.*" in the getidentifer call to return the ids?
You can use some reflection foo to get what you want. It is not pretty at all but it works. The reflection stuff is based on https://gist.github.com/atsushieno/4e66da6e492dfb6c1dd0
private List<string> _stringNames;
private IEnumerable<int> GetIdentifiers(string contains)
{
if (_stringNames == null)
{
var eass = Assembly.GetExecutingAssembly();
Func<Assembly, Type> f = ass =>
ass.GetCustomAttributes(typeof(ResourceDesignerAttribute), true)
.OfType<ResourceDesignerAttribute>()
.Where(ca => ca.IsApplication)
.Select(ca => ass.GetType(ca.FullName))
.FirstOrDefault(ty => ty != null);
var t = f(eass) ??
AppDomain.CurrentDomain.GetAssemblies().Select(ass => f(ass)).FirstOrDefault(ty => ty != null);
if (t != null)
{
var strings = t.GetNestedTypes().FirstOrDefault(n => n.Name == "String");
if (strings != null)
{
var fields = strings.GetFields();
_stringNames = new List<string>();
foreach (var field in fields)
{
_stringNames.Add(field.Name);
}
}
}
}
if (_stringNames != null)
{
var names = _stringNames.Where(s => s.Contains(contains));
foreach (var name in names)
{
yield return Resources.GetIdentifier(name, "string", ComponentName.PackageName);
}
}
}
Then somewhere in your Activity you could do:
var ids = GetIdentifiers("action").ToList();
That will give you all the String Resources, which contain the string action.

Dynamic casting to a generic type

I'm trying to get to the Cases.Values property of System.Activities.Statements.Switch object. The problem is that I cannot cast Activity object at runtime to Switch<> type (which derives from it).
var switchType = activity.GetType();
bool isSwitch = (switchType.IsGenericType && switchType.GetGenericTypeDefinition() == typeof(Switch<>));
if (isSwitch)
{
Type[] genericArgumentTypes = switchType.GetGenericArguments();
if (genericArgumentTypes.Length > 0)
{
var switchStatement = (Switch<genericArgumentTypes[0]>) activity; //that's incorrect
foreach (var aCase in switchStatement.Cases.Values)
{
ProcessActivity(aCase, dataSets, context);
}
}
}
Also,
dynamic switchStatement = activity;
foreach (var aCase in switchStatement.Cases.Values)
{
ProcessActivity(aCase, dataSets, context);
}
throws an error, that the property is not there, while debugger is showing it's not true. The T is irrelevant for me - I need only the Cases collection.
EDIT
Actually, I've found even cleaner solution, than the one I set as an answer.
dynamic switchStatement = activity;
var cases = switchStatement.Cases as IEnumerable;
if (cases != null)
{
foreach (dynamic aCase in cases)
{
ProcessActivity(aCase.Value);
}
}
You can't.
But instead of your loop, put:
var process = typeof(CurrentHelperClass).GetMethod("ProcessSwitch`1").MakeGenericMethod(typeof(genericArgumentTypes[0]));
process.Invoke(null,new object[]{activity});
and define a new method in the same class:
static void ProcessSwitch<T>(Switch<T> switchStatement)
{
foreach (var aCase in switchStatement.Cases.Values)
{
ProcessActivity(aCase, dataSets, context);
}
}
var switchType = activity.GetType();
var prop = switchType.GetProperty("Cases", System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.Instance); //move it outside of the method and use the same property for every time you call the method since it's performance expansive.
bool isSwitch = (switchType.IsGenericType && switchType.GetGenericTypeDefinition() == typeof(Switch<>));
if (isSwitch)
{
IEnumerable dictionary = prop.GetValue(activity,null) as IDictionary;
foreach (var item in dictionary.Values)
{
ProcessActivity(item, dataSets, context);
}
}

Foreach and modification to iteration variable

What's the proper way to do this?
foreach(Object obj in ObjectList<Object>)
{
Object changedObject= GetInfo(obj);
obj=changedObject; //no good
obj.prop1 = changedObject.prop1; //ok?
obj.prop2 = changedObject.prop2; //ok?
better way?
}
Use a for loop instead:
for (int i = 0; i < list.Count; i++)
{
Object changedObject = GetInfo(list[i]);
list[i] = changedObject;
}
You could also use LINQ, and append .ToList() if you need a list instead of an IEnumerable<T>:
var query = result.Select(o => GetInfo(o));
I think you're better off creating a new list of objects, ie:
var newList = new List<Object>();
foreach(Object obj in ObjectList<Object>)
{
newList.Add(GetInfo(obj));
}
Then either replace ObjectList with newList or just return it from whatever you're doing

Categories