I have a need to sort a collection of these based upon criteria determined at run-time.
I was using the code from this article to perform the sorting - originally my code used the dynamic class.
Then I hit issues with serialization over WCF so I switched to using a SerializableDynamicObject and now the sorting code breaks on the line:
PropertyInfo pi = type.GetProperty(prop);
with the error that SerializableDynamicObject does not have a property called "Name" - where "Name" was the value of prop.
I guess the simplest thing to do is to find an alternate way of serializing a dynamic type that the sorting algorithm works with. Any pointers in this direction would be appreciated!
I have looked at this example, but I get the error message:
The constructor with parameters (SerializationInfo, StreamingContext) is not found in ISerializable type
Here's some code using FastMember for this, which works for both reflection-based and dynamic-based objects (depending on what you pass to TypeAccessor.Create)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using FastMember;
namespace ConsoleApplication6
{
class Program
{
static void Main()
{
var list = new List<dynamic>();
dynamic obj = new ExpandoObject();
obj.Foo = 123;
obj.Bar = "xyz";
list.Add(obj);
obj = new ExpandoObject();
obj.Foo = 456;
obj.Bar = "def";
list.Add(obj);
obj = new ExpandoObject();
obj.Foo = 789;
obj.Bar = "abc";
list.Add(obj);
var accessor = TypeAccessor.Create(
typeof(IDynamicMetaObjectProvider));
string propName = "Bar";
list.Sort((x,y) => Comparer.Default.Compare(
accessor[x, propName], accessor[y,propName]));
foreach(var item in list) {
Console.WriteLine(item.Bar);
}
}
}
}
It may be worth mentioining that for reflection-based types, this does not use reflection on a per-item basis; all that is optimized away via meta-programming.
Marc Gravell's answer gave me the answer to complete this - I needed to implement a sorter that could handle multiple sort criteria, not known until runtime. I'm accepting Marc's answer, but posting this as someone may find it useful too.
There may be a more elegant way of achieving this, if so please let me know and I'll update the answer.
public class SerializableDynamicObjectComparer: IComparer
{
private readonly List<KeyValuePair<string, bool>> sortCriteria = new List<KeyValuePair<string, bool>>();
private readonly TypeAccessor accessor;
public SerializableDynamicObjectComparer(IEnumerable<string> criteria)
{
foreach (var criterium in criteria)
{
string[] sortCriterium = criterium.Split('.');
this.sortCriteria.Add(new KeyValuePair<string, bool>(sortCriterium[0],
sortCriterium.Length == 0
? sortCriterium[1].ToUpper() == "ASC"
: false));
}
this.accessor = TypeAccessor.Create(typeof (IDynamicMetaObjectProvider));
}
public int Compare(object x, object y)
{
for(int i=0; i< this.sortCriteria.Count; i++)
{
string fieldName = this.sortCriteria[i].Key;
bool isAscending = this.sortCriteria[i].Value;
int result = Comparer.Default.Compare(this.accessor[x, fieldName], this.accessor[y, fieldName]);
if(result != 0)
{
//If we are sorting DESC, then return the -ve of the default Compare result
return isAscending ? result : -result;
}
}
//if we get here, then objects are equal on all sort criteria.
return 0;
}
}
Usage:
var sorter = new SerializableDynamicObjectComparer(sortCriteria);
var sortableData = reportData.ToList();
sortableData.Sort(sorter.Compare);
where sortCriteria is an array of strings e.g.
new {"Name.DESC", "Age.ASC", "Count"}
Related
I am having trouble comparing two lists using Linq.
My first list contains object instances, which contain a property of interest, to be compared against a second list that contains only values.
Say we have:
List<uint> referenceValues = new List<uint>(){1, 2, 3};
and
List<SomeObject> myObject= new List<SomeObject>();
Now the SomeObject() class has the property "Value" of type uint.
Note that "myObject" list may contains more elements than the "referenceValues" list and we therefore have to select the appropriate portion of "myObject" to be compared against "referenceValues".
I have found the following stack overflow question, which is related, but not quite what I am after: link
Here is what I have tried so far, but without success:
if (myObject
.GetRange((int)offset, count) // Extracted the object of interest from the list
.Where(x => referenceValues.Equals(x.Value)).Count() == 0) // Comparing the "Value" properties against the "referenceValues"
{
// Do something
}
I think I need to use "SequenceEqual" somehow, but I am not really getting how to.
Any advice would be very welcome.
EDIT
Reproducible example:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
int offset = 0;
List<uint> referenceValues = new List<uint>(){1, 2, 3};
List<SomeObject> myObject= new List<SomeObject>(){new SomeObject(){Value=1},
new SomeObject(){Value=2},
new SomeObject(){Value=3}};
if (myObject
.GetRange(offset, referenceValues.Count()-1) // Extracted the object of interest from the list
.Where(x => referenceValues.Equals(x.Value)).Count() == referenceValues.Count()) // Comparing the "Value" properties against the "referenceValues"
{
// Do something
Console.WriteLine("Lists are matching!");
}
else
{
Console.WriteLine("Lists are not matching!");
}
}
}
public class SomeObject
{
public uint Value = 0;
}
EDIT2
Working solution as per suggestion from Guru Stron:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
int offset = 0;
List<uint> referenceValues = new List<uint>(){1, 2, 3};
List<SomeObject> myObject= new List<SomeObject>(){new SomeObject(){Value=1},
new SomeObject(){Value=2},
new SomeObject(){Value=3}};
if (myObject.
GetRange((int)offset, referenceValues.Count())
.Select(someObject => someObject.Value)
.SequenceEqual(referenceValues))
{
// Do something
Console.WriteLine("Lists are matching!");
}
else
{
Console.WriteLine("Lists are not matching!");
}
}
}
public class SomeObject
{
public uint Value = 0;
}
Recommended book about Linq expressions: LINQ Pocket Reference
If you want to check if some subset of myObject values has some concrete order by some property you can do something like this:
bool inOrder = myObject
.GetRange((int)offset, count)
.Select(someObject => someObject.Value)
.SequenceEqual(referenceValues);
If you want to just check that all values are present in the specified referenceValues you can use All (also if source range is empty this returns true):
bool allPresent = myObject
.GetRange((int)offset, count)
.All(someObject => referenceValues.Contains(someObject.Value));
For full collection match without ordering I don't know out of box solution but you can look for one for example here.
if(myObject.Any(x => referenceValues.Contains(x.Value)) {}
I ignored the GetRange part but should make no difference.
I am using Unity and trying remove duplicates from a List using a dictionary key/value pair.
ISSUE:
When the code is run, IF there are duplicates, the first item in the list is deleted and NOT the specific item as "List.Remove(Item item)" should.
public static Dictionary<string, Farm> farmDict = new Dictionary<string, Farm>();
public static List<Farm> farmBuildingsList = new List<Farm>();
public static void UpdateList<T>(List<T> list, Dictionary<string, T> dict, string ID, T resBuilding) where T : ResourceBuildings, new()
{
T obj = new T()
{
ID = resBuilding.ID,
hourlyIncome = resBuilding.hourlyIncome,
baseHour = resBuilding.baseHour,
incomeIncrement = resBuilding.incomeIncrement,
totalSupply = resBuilding.totalSupply
};
if (dict.ContainsKey(ID))
{
dict[ID] = obj;
list.Remove(dict[ID]);
list.Add(dict[ID]);
}
else
{
dict.Add(obj.ID, obj);
list.Add(obj);
}
}
Using Debug.Logs at the if statement, dict[ID] is null, but dict[ID].ID (and all the properties) contains the correct value, so the entire thing is not null. I am not sure if this is the issue, but I've asked for a solution on fixing Object.name within gamedev.stackexchange.
I've read on more appropriate ways (https://www.dotnetperls.com/duplicates and other stackoverflow posts) to remove duplicates using Linq and hashsets, but I can't figure out how to override Equals and HashCode functions.
Short answer
dict[ID] = new T
{
ID = resBuilding.ID,
hourlyIncome = resBuilding.hourlyIncome,
baseHour = resBuilding.baseHour,
incomeIncrement = resBuilding.incomeIncrement,
totalSupply = resBuilding.totalSupply
};
list.RemoveAll(item => item.ID == ID);
list.Add(dict[ID]);
Side note: dict[key] = value is enough to create a new element with the specified key or update an existing one (see the docs), so the whole if (dict.ContainsKey(ID)) is pointless.
Extended answer
Maybe this is a XY Problem, and your current approach could be hiding your real problem-to-be-solved (possibly: I want a dictionary-like structure that shows up in inspector).
I have a
List<object> list = new List<object>();
while (myReader.Read())
{
string arrKablan = myReader["arrK_Title"].ToString();
string arrTotal = myReader["arrTotal"].ToString();
string _title = myReader["MF_Title"].ToString();
string _path = myReader["MF_Path"].ToString();
int _level = Convert.ToInt32(myReader["MF_Level"].ToString());
list.Add(new { title = _title, path = _path, kablanim = arrKablan, total = arrTotal, level = _level });
}
I need to select just items where level == 1
i tried
list = list.where(item => item.level == 1);
but i get an error
'object' does not contain a definition for 'level' and no extension method 'level' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
i know that the compiler can get the type so he can know what it is "level".
how can i achieve this kind of select, without to define a class ?
You have two ways of fixing this:
Use a List<dynamic> instead of a List<object>. This will disable type checks. Drawback: This will disable type checks. :-)
Let the compiler infer the correct type of your list. To do this, have your data layer return a DataTable instead of a DataReader and then use LINQ to create the list:
var myList = (from drow in myDataTable.AsEnumerable()
select new {
kablanim = drow["arrK_Title"].ToString(),
total = drow["arrTotal"].ToString(),
...
}).ToList();
I can't see why you don't just make a concrete class:
public class Foo
{
public string Title { get; set; }
public string Path { get; set; }
// etc, etc
}
Then
List<Foo> list = new List<Foo>();
while (myReader.Read())
{
string arrKablan = myReader["arrK_Title"].ToString();
string arrTotal = myReader["arrTotal"].ToString();
string _title = myReader["MF_Title"].ToString();
string _path = myReader["MF_Path"].ToString();
int _level = Convert.ToInt32(myReader["MF_Level"].ToString());
list.Add(new Foo { Title = _title, Path = _path, /* etc, etc */ });
}
then you call becomes
list = list.Where(item => item.Level == 1).ToList();
(Note the additional ToList call required to make the list assignment valid)
Just for completeness, you can also do this. Create a function to get a value from any object using reflection:
private T GetValue<T>(object obj, string property)
{
return (T)obj.GetType()
.GetProperties()
.Single(p => p.Name == property)
.GetValue(obj);
}
And call it like this:
var filteredList = list.Where(item => GetValue<int>(item, "level") == 1);
You can get value of a property on anonymous class like this:
var anon = new { Level = "level", Time = DateTime.Now };
Type type = anon.GetType();
var props = type.GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.Name == "Level")
{
var x =propertyInfo.GetValue(anon);
}
}
I'm not sure if it is the best way to achieve that, but it is certainly possible.
You are adding object of anonymous class to the list. You can refer to this anonymous class field only inside the method you've defined it in and you should probably avoid adding it to the list, because there is now other way other then reflection or dynamic to access field of theese objects.
For example, you can access one of the elements like this:
var list = new List();
list.Add(new { field1 = "a", field2 = 2 });
list.Add(new { field1 = "b", field2 = 3 });
list.Add(new { field1 = "c", field2 = 4 });
dynamic o = list[1];
Console.WriteLine(o.field1);
Console.WriteLine(o.field2);
But you should be aware, that dynamic feature has a big overhead on every member access.
If you really want to use lambdas, you can rewrite
list = list.where(item => item.level == 1);
like this
var filtered = list.Where(item =>
{
dynamic ditem = item;
return ditem.Level == 1;
});
but this is a bad approach.
The other approach is to use reflection and rewrite this lambda like this
var filtered = list.Where(item =>
{
var field = item.GetType().GetField("level");
return (int)field.GetValue(item) == 1;
});
This is better than using dynamic because it has a smaller overhead, but can still be very costly.
Also it would probably be better to cache FieldInfo object outside of loop if your anonymous objects have same type. It can be done like this
var field = list.First().GetType().GetField("level");
var filtered = list.Where(item => (int)field.GetValue(item) == 1);
For performance reasons, Linq depends on metadata being available at compile time. By explicitly declaring List<object> you have typed the elements of this list as object which does not have a member level.
If you want to use Linq like this you have two options.
Declare a class with a level member and use it to type the collection
Declare an interface with a level member and use it to cast in the lambda expression
Option 1 is the preferred approach. Normally Linq is used with a database and the classes are generated by Visual Studio directly from the database. This is why nobody complains about the need for classes to supply metadata.
The following line creates anonymous class.
new { title = _title, path = _path, kablanim = arrKablan, total = arrTotal, level = _level });
You can't cast then your objects to anything meaningfull.
Objects don't have those properties.
You have to create a class by your own and use it.
What alternatives are there to property.setvalue()? I've read that it is very slow. I'm using it to map IDataReader to POCO objects.
This is a truncated version of the code. Everything here is very new to me. I know there a lot of frameworks that accomplish this task. However, we can't use them.
public class DbAutoMapper<T>
{
public IEnumerable<T> MapToList(IDataReader reader)
{
var list = new List<T>();
while (reader.Read())
{
var obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
foreach (var attribute in prop.GetCustomAttributes(true))
{
prop.SetValue(obj, value, null);
}
}
list.Add(obj);
}
return list;
}
}
Firstly: why are you repeating the reflection for every attribute, when you don't use the attribute?
Second: assuming you intended to map this by name, column-to-property (which isn't what the code currently does), consider a tool like dapper, which does all this for you, including cached high-performance reflection-emit. It'll also handle the command itself for you. For example:
string region = "North";
var customers = conn.Query<Customer>(
#"select * from Customers where Region = #region",
new { region } // full parameterization, the easy way
).ToList();
If you need more control, consider FastMember, which provides fast member-access (again, reflection-emit), but without being specific to data access:
var accessor = TypeAccessor.Create(typeof(T));
string propName = // something known only at runtime
while( /* some loop of data */ ) {
var obj = new T();
foreach(var col in cols) {
string propName = // ...
object cellValue = // ...
accessor[obj, propName] = cellValue;
}
yield return obj;
}
A few approaches come to mind...
Skip Reflection
public class DbAutoMapper<T> where T : IInitFromReader, new()
{
public IEnumerable<T> MapToList(IDataReader reader)
{
var list = new List<T>();
while (reader.Read())
{
IInitFromReader obj = new T;
obj.InitFromReader(reader);
list.Add(obj);
}
return list;
}
}
Then you'll have to implement the InitFromReader in each of your entitiy objects. Obviously, this skips the benefits of reflection (less code).
Code Generation
Maintaining this code for (InitFromReader) is painful, so you could opt to generate it. This in many ways gives you the best of both worlds:
You don't have to maintain (by hand) a lot of code
You don't take the performance hit of reflection.
Foo is a class with a lot of string fields. I want to create a method Wizardify that performs an operation on many of the fields of the object. I could do it like this:
Foo Wizardify(Foo input)
{
Foo result;
result.field1 = Bar(input.field1);
result.field2 = Bar(input.field2);
result.field3 = Bar(input.field3);
...
This is some easily generated code, but I prefer not to waste fifty lines on this. Is there a way to go over selected fields of an object? Note that there are four or five fields I want to work on in a different way and they should be excluded from the iteration.
try
foreach ( FieldInfo FI in input.GetType().GetFields () )
{
FI.GetValue (input)
FI.SetValue (input, someValue)
}
Though I would not recommend the reflection approach for known Types - it is slow and depending on your specific scenario could pose some permission issue at runtime...
This is what I have - it gives me a list (names) of all properties in my classes, that later I can work on with Reflection or "Expression trees":
private static string xPrev = "";
private static List<string> result;
private static List<string> GetContentPropertiesInternal(Type t)
{
System.Reflection.PropertyInfo[] pi = t.GetProperties();
foreach (System.Reflection.PropertyInfo p in pi)
{
string propertyName = string.Join(".", new string[] { xPrev, p.Name });
if (!propertyName.Contains("Parent"))
{
Type propertyType = p.PropertyType;
if (!propertyType.ToString().StartsWith("MyCms"))
{
result.Add(string.Join(".", new string[] { xPrev, p.Name }).TrimStart(new char[] { '.' }));
}
else
{
xPrev = string.Join(".", new string[] { xPrev, p.Name });
GetContentPropertiesInternal(propertyType);
}
}
}
xPrev = "";
return result;
}
public static List<string> GetContentProperties(object o)
{
result = new List<string>();
xPrev = "";
result = GetContentPropertiesInternal(o.GetType());
return result;
}
Usage: List<string> myProperties = GetContentProperties(myObject);
Loop through typeof(YourType).GetProperties() and call GetValue or SetValue.
Note that reflection is rather slow.
You could use the Dynamic Language Runtime to generate a lambda of the type Func. You'll just need to generate the lambda once (you can cache it away) and there'll be no reflection performance hit.