Resolve property at runtime. C# - c#

I have some class which have some code
public IEnumerable<TypeOne> TypeOne
{
get
{
if (db != null)
{
var col = db.Select<TypeOne>();
if (col.Count > 0) return col;
}
return db2.TypeOne;
}
}
public IEnumerable<TypeTwo> TypeTwo
{
get
{
if (db != null)
{
var col = db.Select<TypeTwo>();
if (col.Count > 0) return col;
}
return db2.TypeTwo;
}
}
So as You can see there is a lot of duplicated Code and there are same property name and item type of enumerable.
I want to call some property of object like "obj.MyProp". And MyProp must be resolved at runtime with some generic or non-generic method. Is it possible?

Slightly incomplete answer but you'll get the general idea:
This is a scenario where you want generics.
public IEnumerable<t> TypeSomething
{
get
{
if (db != null)
{
t col = db.Select<t>();
if (col.Count > 0) return col;
}
return GetDB<t>();
}
}
You'd need to implement GetDB() to return the appropriate db for any given type, but that'd be a single switch (or you can use reflection to find it)

There are a couple of ways to do this. The best is probably a generic method:
public IEnumerable<T> dbSelect<T>() //may need type constraints here
{
return db != null
? db.Select<T>()
: null;
}
public IEnumerable<TypeOne> TypeOne
{
get { return dbSelect<TypeOne> ?? db2.TypeOne; }
}
public IEnumerable<TypeTwo> TypeTwo
{
get { return dbSelect<TypeTwo>() ?? db2.TypeTwo; }
}
If your db2 object has a generic Select<T>-type method like db does, it's even easier:
public IEnumerable<T> dbSelect<T>()
{
return db != null
? db.Select<T>()
: db2.Select<T>(); //or db2.GetEntities<T>() or db2.OfType<T> or whatever
}
//Later, in your main code...
var x = dbSelect<TypeOne>();
var y = dbSelect<TypeTwo>();
This will be type safe, considerably faster than reflection, and will work with Intellisense.

You can solve this by using generics:
public IEnumerable<TypeOne> TypeOne
{
get { return GetTable<TypeOne>(); }
}
public IEnumerable<TypeTwo> TypeTwo
{
get { return GetTable<TypeTwo>(); }
}
private IEnumerable<T> GetTable<T>()
{
if (db != null)
{
var col = db.Select<T>();
if (col.Count > 0) return col;
}
return db2.Select<T>();
}

Related

Ternary operator difficult to read

Any suggestion how to make the below query more "readable"?
var result = result
.OrderBy(a =>
(conditionA) ?
valueA :
(conditionB ? valueB :
(conditionC ?
(conditionD ?
valueC : valueD) :
valueE)));
Its difficult to read with the long code of condition and value.
There are several ways of improving the readability of your code.
Indentation
One way is to indent the code in a slightly different way, but this only helps readability a little:
var result = result.OrderBy(a =>
conditionA ? valueA :
conditionB ? valueB :
conditionC ? conditionD ? valueC :
valueD :
valueE);
if, else
You could also turn those ternary operators into a more readable chain of if, else.
var result = Result.OrderBy(a => {
if (conditionA)
{
return valueA;
}
else if (conditionB)
{
return valueB;
}
else if (conditionC)
{
if (conditionD)
{
return valueC;
}
else
{
return valueD;
}
}
else
{
return valueE;
}
});
IComparer<>
One option would be to write your own implementation of IComparer<> and pass it to the OrderBy method. I don't know what type your object is or what type the keys are in your code, so I'm going to assume string keys.
public class MyClassComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
string xKey = getKey(x);
string yKey = getKey(y);
return string.Compare(xKey, yKey);
}
private string getKey(MyClass item)
{
if (item.conditionA)
{
return item.valueA;
}
else if (item.conditionB)
{
return item.valueB;
}
else if (item.conditionC)
{
if (item.conditionD)
{
return item.valueC;
}
else
{
return item.valueD;
}
}
else
{
return item.valueE;
}
}
}
Extension method
A final option would be to move your code to an extension method:
public static class MyClassExtensions
{
public static string GetSortingKey(this MyClass item)
{
if (item.conditionA)
{
return item.valueA;
}
else if (item.conditionB)
{
return item.valueB;
}
else if (item.conditionC)
{
if (item.conditionD)
{
return item.valueC;
}
else
{
return item.valueD;
}
}
else
{
return item.valueE;
}
}
}
Using the last option, your call to OrderBy is simply:
result.OrderBy(a => a.GetSortingKey())

Implement Filtering with Custom Adapter in Android with C#

I'm trying to implement Filter in ActionBar with Custom Adapter.
My custom Adapter implements IFilterable. And my Filter getter in custom adapter looks like:
public Filter Filter {
get
{
if (tdFilter == null){
tdFilter = new TodoItemFilter();
tdFilter.OriginalData = this._originaltodoItemList;
tdFilter.TdAdapter = this;
}
return tdFilter;
}
}
I have created an ItemFilter class.
My overriden PerformFiltering method in ItemFilter.cs code looks like:
protected override Filter.FilterResults PerformFiltering(Java.Lang.ICharSequence constraint)
{
FilterResults oreturn = new FilterResults();
if (constraint == null || constraint.Length() == 0)
{
oreturn.Values = this.OriginalData.ToJavaObject();
oreturn.Count = this.OriginalData.Length;
}
else
{
string[] actualResults = new string[this.originalData.Length];
List<TodoItem> tdiList = new List<TodoItem> ();
int i=0;
foreach (TodoItem td in this.originalData)
{
if(td.Name.ToUpperInvariant().StartsWith(constraint.ToString().ToUpperInvariant())){
tdiList.Add (td);
i++;
}
}
oreturn.Values = tdiList.ToJavaObject();
oreturn.Count = tdiList.Count;
}
return oreturn;
}
My overriden Publishresults method looks like:
protected override void PublishResults(Java.Lang.ICharSequence constraint, Filter.FilterResults results)
{
if (results.Count == 0)
this.TdAdapter.NotifyDataSetInvalidated();
else
{
var propertyInfo = ((JavaHolder)results.Values).Instance;
TdAdapter.NotifyDataSetChanged();
}
}
And my JavaHolder class looks like:
public class JavaHolder : Java.Lang.Object
{
public readonly object Instance;
public JavaHolder(object instance)
{
Instance = instance;
}
}
My "PerformFiltering" method is working fine.
But, in my "PublishResults" method, I'm unable to convert the results.Values to my .NET object (TodoItem[]). TdAdapter is my Custom adapter.
TdAdapter._originaltodoItemList = ((JavaHolder)results.Values).Instance;
Can someone please point me to the right direction?
Am I missing something?
Please help.
Got it to work.
My "PublishResults" method now looks like this:
protected override void PublishResults(Java.Lang.ICharSequence constraint, Filter.FilterResults results)
{
if (results.Count == 0)
this.TdAdapter.NotifyDataSetInvalidated();
else
{
System.Object obj = results.Values.ToNetObject<System.Object>();
IEnumerable enumerable = obj as IEnumerable;
List<TodoItem> LTdi = new List<TodoItem> ();
if (enumerable != null)
{
foreach(object element in enumerable)
{
LTdi.Add (element as TodoItem);
}
}
TdAdapter._originaltodoItemList = LTdi.ToArray();
TdAdapter.NotifyDataSetChanged();
}
}
And it is working perfectly as expected.

Howto search through Properties of all kinds of types

I have a base class called Part and derived classes like Wire or Connector and many more that inherit from Part.
Now I want to implement a search function that searches all Properties of the derived classes for a string.
If necessary that string should be tried to be converted to the type of the Property. The Properties can also be Lists and should be searched on the first level.
class Part
{
public int Id { get; set; }
public string Name { get; set; }
}
class Wire : Part
{
public NumberWithUnit Diameter { get; set; }
public Weight Weight { get; set; }
}
class Connector : Part
{
public List<Part> ConnectedParts { get; set; }
}
I know how to generally search through the Properties of base types with Reflection like this
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is string)
{
if (string.Equals(value, searchString))
return true;
}
else if (value is int)
{
int v;
if (int.TryParse(searchString, out v))
{
if(v == (int) value)
return true;
}
}
}
return false;
}
But that would be a long list of types and I have Properties of Type Weight for instance and many more. Is there some kind of general way to search without casting all types?
Consider going the opposite direction with your conversion. Rather than converting your search string into each possible value, just convert the value into a string:
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is IEnumerable)
{
// special handling for collections
}
else if(value != null)
{
string valueString = value.ToString();
if (string.Equals(valueString, searchString))
return true;
}
}
return false;
}
Besides working pretty well for most built-in types, the only thing you have to do to get it to work for Weight, etc. is make sure they implement ToString().
Another solution would be to use TypeDescriptor:
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is IEnumerable)
{
// special handling for collections
}
else if(value != null)
{
object searchValue = null;
try
{
searchValue = TypeDescriptor.GetConverter(value).ConvertFromString(searchString);
} catch {}
if (searchValue != null && object.Equals(value, searchValue))
return true;
}
}
return false;
}
TypeDescriptor works well for most built-in types, but requires extra work if you're dealing with custom types.
I think the following should cover the most of the practical scenarios:
public static bool SearchProperties(object target, string searchString)
{
if (target == null) return false;
// Common types
var convertible = target as IConvertible;
if (convertible != null)
{
var typeCode = convertible.GetTypeCode();
if (typeCode == TypeCode.String) return target.ToString() == searchString;
if (typeCode == TypeCode.DBNull) return false;
if (typeCode != TypeCode.Object)
{
try
{
var value = Convert.ChangeType(searchString, typeCode);
return target.Equals(value);
}
catch { return false; }
}
}
if (target is DateTimeOffset)
{
DateTimeOffset value;
return DateTimeOffset.TryParse(searchString, out value) && value == (DateTimeOffset)target;
}
var enumerable = target as IEnumerable;
if (enumerable != null)
{
// Collection
foreach (var item in enumerable)
if (SearchProperties(item, searchString)) return true;
}
else
{
// Complex type
var properties = target.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetMethod == null || property.GetMethod.GetParameters().Length > 0) continue;
var value = property.GetValue(target);
if (SearchProperties(value, searchString)) return true;
}
}
return false;
}
I will give you one different idea to do it.
You could try something like that:
private bool SearchProperties<T, W>(T part, W searchValue) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
if (typeof(W) == prop.PropertyType)
{
var value = prop.GetValue(part, null);
if (searchValue.Equals(value))
return true;
}
}
return false;
}
You need to call the method like this:
private void button12_Click(object sender, EventArgs e)
{
Part p = new Part();
p.Id = 2;
p.Name = "test";
p.bla = new Bla();
SearchProperties<Part, int>(p, 2);
}
And if you need to compare the complex properties (Weight, ...) by a different way from GetHashCode you could override the method Equals or the == operator.
class Weight
{
public int Id { get; set; }
public override bool Equals(object obj)
{
return Id == ((Weight)obj).Id;
}
}

How to get the Count property using reflection for Generic types

I have a list of objects, of which I cannot know the type of at compile-time.
I need to identify any of these objects where a 'Count' property exists, and get the value if it does.
This code works for simple Collection types:
PropertyInfo countProperty = objectValue.GetType().GetProperty("Count");
if (countProperty != null)
{
int count = (int)countProperty.GetValue(objectValue, null);
}
The problem is that this doesn't work for generic types, such as IDictionary<TKey,TValue>. In those cases, the 'countProperty' value is returned as null, even though a 'Count' property exists in the instanced object.
All I want to do is identify any collection/dictionary based object and find the size of it, if it has one.
Edit: as requested, here's the entire listing of code that doesn't work
private static void GetCacheCollectionValues(ref CacheItemInfo item, object cacheItemValue)
{
try
{
//look for a count property using reflection
PropertyInfo countProperty = cacheItemValue.GetType().GetProperty("Count");
if (countProperty != null)
{
int count = (int)countProperty.GetValue(cacheItemValue, null);
item.Count = count;
}
else
{
//poke around for a 'values' property
PropertyInfo valuesProperty = cacheItemValue.GetType().GetProperty("Values");
int valuesCount = -1;
if (valuesProperty != null)
{
object values = valuesProperty.GetValue(cacheItemValue, null);
if (values != null)
{
PropertyInfo valuesCountProperty = values.GetType().GetProperty("Count");
if (countProperty != null)
{
valuesCount = (int)valuesCountProperty.GetValue(cacheItemValue, null);
}
}
}
if (valuesCount > -1)
item.Count = valuesCount;
else
item.Count = -1;
}
}
catch (Exception ex)
{
item.Count = -1;
item.Message = "Exception on 'Count':" + ex.Message;
}
}
This works OK on simple collections, but not on an object created from a class I have which is derived from Dictionary<TKey,TValue>. Ie
CustomClass :
Dictionary<TKey,TValue>
CacheItemInfo is just a simple class that contains properties for cache items - ie, key, count, type, expiration datetime
The first thing you should try is casting to ICollection, as this has a very cheap .Count:
ICollection col = objectValue as ICollection;
if(col != null) return col.Count;
The Count for dictionary should work though - I've tested this with Dictionary<,> and it works fine - but note that even if something implements IDictionary<,>, the concrete type (returned via GetType()) doesn't have to have a .Count on the public API - it could use explicit interface implementation to satisfy the interface while not having a public int Count {get;}. Like I say: it works for Dictionary<,> - but not necessarily for every type.
As a last ditch effort if everything else fails:
IEnumerable enumerable = objectValue as IEnumerable;
if(enumerable != null)
{
int count = 0;
foreach(object val in enumerable) count++;
return count;
}
Edit to look into the Dictionary<,> question raised in comments:
using System;
using System.Collections;
using System.Collections.Generic;
public class CustomClass : Dictionary<int, int> { }
public class CacheItemInfo
{
public int Count { get; set; }
public string Message { get; set; }
}
class Program {
public static void Main() {
var cii = new CacheItemInfo();
var data = new CustomClass { { 1, 1 }, { 2, 2 }, { 3, 3 } };
GetCacheCollectionValues(ref cii, data);
Console.WriteLine(cii.Count); // expect 3
}
private static void GetCacheCollectionValues(ref CacheItemInfo item, object cacheItemValue)
{
try
{
ICollection col;
IEnumerable enumerable;
if (cacheItemValue == null)
{
item.Count = -1;
}
else if ((col = cacheItemValue as ICollection) != null)
{
item.Count = col.Count;
}
else if ((enumerable = cacheItemValue as IEnumerable) != null)
{
int count = 0;
foreach (object val in enumerable) count++;
item.Count = count;
}
else
{
item.Count = -1;
}
}
catch (Exception ex)
{
item.Count = -1;
item.Message = "Exception on 'Count':" + ex.Message;
}
}
}
How about adding this after your first check (!untested!) ...
foreach (Type interfaceType in objectValue.GetType().GetInterfaces())
{
countProperty = interfaceType.GetProperty("Count");
//etc.
}

Does .NET have a built in IEnumerable for multiple collections?

I need an easy way to iterate over multiple collections without actually merging them, and I couldn't find anything built into .NET that looks like it does that. It feels like this should be a somewhat common situation. I don't want to reinvent the wheel. Is there anything built in that does something like this:
public class MultiCollectionEnumerable<T> : IEnumerable<T>
{
private MultiCollectionEnumerator<T> enumerator;
public MultiCollectionEnumerable(params IEnumerable<T>[] collections)
{
enumerator = new MultiCollectionEnumerator<T>(collections);
}
public IEnumerator<T> GetEnumerator()
{
enumerator.Reset();
return enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
enumerator.Reset();
return enumerator;
}
private class MultiCollectionEnumerator<T> : IEnumerator<T>
{
private IEnumerable<T>[] collections;
private int currentIndex;
private IEnumerator<T> currentEnumerator;
public MultiCollectionEnumerator(IEnumerable<T>[] collections)
{
this.collections = collections;
this.currentIndex = -1;
}
public T Current
{
get
{
if (currentEnumerator != null)
return currentEnumerator.Current;
else
return default(T);
}
}
public void Dispose()
{
if (currentEnumerator != null)
currentEnumerator.Dispose();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
if (currentIndex >= collections.Length)
return false;
if (currentIndex < 0)
{
currentIndex = 0;
if (collections.Length > 0)
currentEnumerator = collections[0].GetEnumerator();
else
return false;
}
while (!currentEnumerator.MoveNext())
{
currentEnumerator.Dispose();
currentEnumerator = null;
currentIndex++;
if (currentIndex >= collections.Length)
return false;
currentEnumerator = collections[currentIndex].GetEnumerator();
}
return true;
}
public void Reset()
{
if (currentEnumerator != null)
{
currentEnumerator.Dispose();
currentEnumerator = null;
}
this.currentIndex = -1;
}
}
}
Try the SelectMany extension method added in 3.5.
IEnumerable<IEnumerable<int>> e = ...;
foreach ( int cur in e.SelectMany(x => x)) {
Console.WriteLine(cur);
}
The code SelectMany(x => x) has the effect of flattening a collection of collections into a single collection. This is done in a lazy fashion and allows for straight forward processing as shown above.
If you only have C# 2.0 available, you can use an iterator to achieve the same results.
public static IEnumerable<T> Flatten<T>(IEnumerable<IEnumerable<T>> enumerable) {
foreach ( var inner in enumerable ) {
foreach ( var value in inner ) {
yield return value;
}
}
}
Just use the Enumerable.Concat() extension method to "concatenate" two IEnumerables. Don't worry, it doesn't actually copy them into a single array (as you might infer from the name), it simply allows you to enumerate over them all as if they were one IEnumerable.
If you have more than two then Enumerable.SelectMany() would be better.

Categories