How to pass anonymous property via lambda to a generic function? - c#

I want to pass anonymous property using lambda to a generic function and access it there.
And how Do I access the property inside.
using (CommentsRepository commentsRepository = new CommentsRepository())
{
var comments = commentsRepository.GetAllComments();
Foo<Comment>(comments, 0,com=>com.ID); //Comment is an EF entity
}
public static void Foo<TObject>(IEnumerable<TObject> list, int iCurID, <Func<TObject, TProperty> property) where TObject : class
{
foreach (var cat in list.Where(/*How to access the property*/==iCurID)
{
int x = cat.property;
}
}

You just call the delegate:
public static void Foo<TObject>
(IEnumerable<TObject> list,
int iCurID,
Func<TObject, int> propertySelector) where TObject : class
{
foreach (var cat in list.Where(x => propertySelector(x) == iCurID))
{
}
}
Note that I had to change the type of the delegate to Func<TObject, int> as otherwise you couldn't compare it with iCurID.

Related

Accessing class fields throughout generic function (.Net Framework 4.8)

First, I have this function that return true if the name is in the class:
public bool hasName<T>(List<T> Data, string name, Func<T, string> ClassName)
{
foreach (T entry in Data)
{
if (ClassName(entry) == name)
{
return true;
}
}
return false;
}
And it's called using:
hasName(Data, name, x => x.name)
The problem is, that i have another function that uses HasName but doesn't know about the field name.
public List<T> MergeClasses<T>(List<T> Pri, List<T> Sec, Func<T, string> ClassName)
{
List<T> result = new List<T>();
result.AddRange(Pri);
foreach (T entry in Sec)
{
if (!new Functions().hasName(result, ClassName(entry), x => x.name))
{
result.Add(entry);
}
}
return result;
}
How can i solve this?
You would need an interface or base-class to use as a generic constraint; for example:
interface IHazHame {
string Name {get;} // note property, not field
}
then your type with .name would need to implement that:
class Foo : IHazName {
// ...
}
and you can restrict your generic method to T that satisfy this:
public List<T> MergeClasses<T>(List<T> Pri, List<T> Sec, Func<T, string> ClassName)
where T : IHazHame
{
// ... x => x.Name
}

Build dynamic predicate based on generic type

How do I make this expression dynamic based on the generic type passed in the parameter?
In the simplified form:
public static class CompareService
{
public static List<T> Run<T>(List<T> database_list, string directory_path)
{
var csv_list = CompareService.MergeRecordsFromFiles<T>(directory);
return CompareService.RunComparison<T>(database_list, csv_list);
}
public static T CompareData<T>(List<T> database_list, List<T> csv_list)
{
var diff = new List<T>();
foreach (var db_item in database_list)
{
// ...
// if T is of type Deathstar compare reference_number property
// if T is of type Stormtrooper compare id property
// if T is of type Sith compare id and anger_level property
var csv_item = csv_list.FirstOrDefault(x => x.reference_number == db_item.reference_number);
// Comparison code
ComparisonResult result = compareLogic.Compare(db_item, csv_item);
// ...
}
return diff;
}
}
It is called from another generic service:
public static void Whatever<T>(List<T> list)
{
// ...
var directory_path = "C:\";
var delta = CompareService.CompareData<T>(list, directory_path);
// ...
}
The most naive implementation would be to check if your itemToFind can be cast to DeathStar, StormTrooper or Sith and if so call the instances property.
var deathStar = itemToFind as DeathStar;
if(deathStar != null)
return database_list.Where(x => ((DeathStar)x).reference_number == deathStar.reference_number).FirstOrDefault();
else
{
var sith = itemToFind as Sith;
if(sith != null)
return database_list.Where(x => ((Sith)x).anger_level == sith.anger_level).FirstOrDefault();
else
return database_list.Where(x => ((StormTrooper)x).id== ((StormTrooper)item).id).FirstOrDefault();
}
This is quite cumbersome, including many casts. In particular it completely bypasses the actual benefits of generics using any arbitrary type (that fullfills the constraints if existing). In your case you´d have a generic method that will only wortk for three decent types.
A better approach is to let all your classes implement a common interface that defines a property, for instance:
interface IObject {
int Level { get; }
}
Now all classes define that level-property:
clas DeathStar : IObject
{
public int Level { get { return this.reference_number; } }
}
clas Sith : IObject
{
public int Level { get { return this.anger_level; } }
}
clas StormTrooper: IObject
{
public int Level { get { return this.id; } }
}
Than you can use a constraint on your type T to implement that interface:
public static T CompareData<T>(List<T> list, T itemToFind) where T: IObject
Why not like this:
public static T CompareData<T>(List<T> list, Func<T, bool> predicate)
{
return database_list.FirstOrDefault(predicate);
}
And then use it like this:
var itemToFind = new ItemToFind();
var myObjectList = new List<MyObject>();
var item = CompareData<MyObject>(myObjectList, x=> x.MyObjectProperty == itemToFind.Id);
You could add a property selector:
public static class CompareService
{
public static T CompareData<T>(this List<T> list, T itemToFind, Func<T, int> propSelector)
{
int propToFind = propSelector(itemToFind); // cache
return database_list.FirstOrDefault(x => propSelector(x) == propToFind);
}
}
And call it like that:
listOfDeathstars.CompareData(deathStarToFind, ds => ds.reference_number);
listOfStormtroopers.CompareData(trooperToFind, t => t.id);
listOfSiths.CompareData(sithStarToFind, sith => new { sith.id, sith.anger_level});
Note: I added the this keyword in the signature to make it an extension (not sure if you intended that but forgot the keyword). And Where(predicate).FirstOrDefault() can be reduced to FirstOrDefault(predicate).

C# List the class property in lambda expression

I am trying to create an extension method to list the properties in the lambda expression.
Let say there is a Class named Example
public class Example {
Public string Name {get;set;}
Public string Description {get;set;}
}
the extension method can be something like below
public static void GetProperties<T>(this T obj) where T : new()
{
}
Expected usage : this.GetProperties<Example>(m=>m.
so when i type m=>m. should display both the properties (Name,Description).
I think you need to used Func:
public static void GetProperties<T, V>(this T obj, Func<T, V selector) where T : new()
{
}
Usage:
Example ex = new Example();
ex.GetProperties(m => m.Name); // Func<Example, string>
ex.GetProperties(m => m.Description); // Func<Example, string>
I really didn't understand the expected behavior inside the method. But you mentioned m.Name and m.Description. So a property selector is your way.
Func<Example, string> is a function that accepts an Example input parameter and returns a string (which is the property in case of Name and Description).
public static class PropertyUtility
{
public static string GetPropertyName<T>(this T entity, Expression<Func<T, object>> exp)
{
if (exp.Body is MemberExpression) {
return ((MemberExpression)exp.Body).Member.Name;
}
else {
var op = ((UnaryExpression)exp.Body).Operand;
return ((MemberExpression)op).Member.Name;
}
}
}
And use like this:
Example ex = new Example();
var property = ex.GetPropertyName(x => x.Description);

c#: Call boxed delegate

In my project I need to transform data between several classes so I created a class DataMapper that is used for strong-typed mapping of properties from two different classes. When properties in the pair need to be modified I store two delegates (converters) for this purpose.
Then the DataMapper has two methods Update(T source, S target) and Update(S source, T target) that use these mappings to provide the tranformation.
public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType> {
private readonly IDictionary<PropertyInfo, PropertyInfo> _sourceToTargetMap = new Dictionary<PropertyInfo, PropertyInfo>();
private readonly IDictionary<PropertyInfo, object> _converters = new Dictionary<PropertyInfo, object>();
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
{
_sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
return this;
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
Func<TSourceValue, TTargetValue> sourceToTargetConverter,
Func<TTargetValue, TSourceValue> targetToSourceConverter)
{
_sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
_converters.Add(sourcePropExpr.AsPropertyInfo(), sourceToTargetConverter);
_converters.Add(targetPropExpr.AsPropertyInfo(), targetToSourceConverter);
return this;
}
public void Update(TSourceType source, TTargetType target) {
foreach (var keyValuePair in _sourceToTargetMap) {
var sourceProp = keyValuePair.Key;
var targetProp = keyValuePair.Value;
Update(source, target, sourceProp, targetProp);
}
}
public void Update(TTargetType source, TSourceType target) {
foreach (var keyValuePair in _sourceToTargetMap) {
var sourceProp = keyValuePair.Value;
var targetProp = keyValuePair.Key;
Update(source, target, sourceProp, targetProp);
}
}
private void Update(
object source,
object target,
PropertyInfo sourceProperty,
PropertyInfo targetProperty)
{
var sourceValue = sourceProperty.GetValue(source);
if (_converters.ContainsKey(sourceProperty)) {
sourceValue = typeof(InvokeHelper<,>)
.MakeGenericType(sourceProperty.PropertyType, targetProperty.PropertyType)
.InvokeMember("Call", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new[] { _converters[sourceProperty], sourceValue });
}
targetProperty.SetValue(target, sourceValue);
}
}
Here is the usage:
public SomeClass {
private static readonly IDataUpdater<SomeClass, SomeOtherClass> _dataMapper = new DataMapper<SomeClass, SomeOtherClass>()
.Map(x => x.PropertyA, y => y.PropertyAA)
.Map(x => x.PropertyB, y => y.PropertyBB, x => Helper.Encrypt(x), y => Helper.Decrypt(y));
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public void LoadFrom(SomeOtherClass source) {
_dataMapper.Update(source, this);
}
public void SaveTo(SomeOtherClass target) {
_dataMapper.Update(this, target);
}
}
You can see in class DataHelper in the last overload of method Update that when I want to call the stored converter function, I use helper class InvokeHelper, because I didn't found other way how to call boxed delegate Func. Code for class InvokeHelper is simple - just single static method:
public static class InvokeHelper<TSource, TTarget> {
public static TTarget Call(Func<TSource, TTarget> converter, TSource source) {
return converter(source);
}
}
Is there a way how to do it without reflection? I need to optimalize these transformations for speed.
Thanks.
You can use Delegate.DynamicInvoke to invoke the delegate. Or, use dynamic:
((dynamic)(Delegate)_converters[sourceProperty])(sourceValue);
The (Delegate) cast is not necessary. It's for documentation and runtime assertion purposes. Leave it out if you don't like it.
Actually, you better use delegate instead of object in the dictionary.
If it were me, I would use a little meta-coding with expressions to create a list of compiled and strongly typed delegates. When you call the Update method, you can go through each Action in the list and update the destination from the source.
No reflection and all of the compiling and such is done once, ahead of the Update call.
public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType>
{
List<Action<TSourceType, TTargetType>> _mappers = new List<Action<TSourceType, TTargetType>>();
DataMapper<TTargetType, TSourceType> _reverseMapper;
public DataMapper() : this(false) { }
public DataMapper(bool isReverse)
{
if (!isReverse)
{
_reverseMapper = new DataMapper<TTargetType, TSourceType>(isReverse: true);
}
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
{
var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body);
_mappers.Add(
Expression.Lambda<Action<TSourceType, TTargetType>>(
mapExpression,
sourcePropExpr.Parameters[0],
targetPropExpr.Parameters[0])
.Compile());
if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr);
return this;
}
public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
Func<TSourceValue, TTargetValue> sourceToTargetConverter,
Func<TTargetValue, TSourceValue> targetToSourceConverter)
{
var convertedSourceExpression = Expression.Invoke(Expression.Constant(sourceToTargetConverter), sourcePropExpr.Body);
var mapExpression = Expression.Assign(targetPropExpr.Body, convertedSourceExpression);
_mappers.Add(
Expression.Lambda<Action<TSourceType, TTargetType>>(
mapExpression,
sourcePropExpr.Parameters[0],
targetPropExpr.Parameters[0])
.Compile());
if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr, targetToSourceConverter, sourceToTargetConverter);
return this;
}
public void Update(TSourceType source, TTargetType target)
{
foreach (var mapper in _mappers)
{
mapper(source, target);
}
}
public void Update(TTargetType source, TSourceType target)
{
if (_reverseMapper != null)
{
_reverseMapper.Update(source, target);
}
else
{
throw new Exception("Reverse mapper is null. Did you reverse twice?");
};
}
}
The expression is built by taking the expressions that are passed in and using them as parts for the new expression.
Say you called .Map(x => x.PropertyA, y => y.PropertyAA). You now have 2 expressions each with a parameter x and y and each with a body x.PropertyA and y.PropertyAA.
Now you want to re-assemble these expression parts into an assignment expression like y.PropertyAA = x.PropertyA. This is done in the line var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body); which gives you an expected expression.
Now when you call Expression.Lambda, you are incorporating the parameters (x,y) into a new expression that looks like (x,y) = > y.PropertyAA = x.PropertyA.
Before you can execute this, you need to compile it, hence the .Compile(). But since you only need to compile this once for any given map, you can compile and store the result. The uncompiled expression is of type Expression<Action<TSourceType,TTargetType>> and after it is compiled the resulting type is Action<TSourceType,TTargetType>

Returning generic parameter with yield return

Hopefully this isn't a dupe, couldn't find anything related online
I'm getting a strange compile time error in the following extension method:
public static TCol AddRange<TCol, TItem>(this TCol e, IEnumerable<TItem> values)
where TCol: IEnumerable<TItem>
{
foreach (var cur in e)
{
yield return cur;
}
foreach (var cur in values)
{
yield return cur;
}
}
Error:
The body of 'TestBed.EnumerableExtensions.AddRange(TCol, System.Collections.Generic.IEnumerable)' cannot be an iterator block because 'TCol' is not an iterator interface type
Does this mean that generic constraints are not considered by the compiler when determining if a method qualifies for yield return use?
I use this extension method in a class which defines the collection using a generic parameter. Something like (in addition to a few type cast operators):
public class TestEnum<TCol, TItem>
where TCol : class, ICollection<TItem>, new()
{
TCol _values = default(TCol);
public TestEnum(IEnumerable<TItem> values)
{
_values = (TCol)(new TCol()).AddRange(values);
}
public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
...
}
And in turn, used like (remember I have type cast operators defined):
TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
Originally, my extension method looked like:
public static IEnumerable<TItem> AddRange<TItem>(this IEnumerable<TItem> e, IEnumerable<TItem> values)
{
...
}
Which compiles but results in:
Unhandled Exception: System.InvalidCastException: Unable to cast object of type '<AddRange>d__61[System.String]' to type 'System.Collections.Generic.List1[System.String]'.
Is there an alternative way to do this?
As requested, here's a small sample:
class Program
{
public static void Main()
{
TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
}
}
public class TestEnum<TCol, TItem>
where TCol : class, ICollection<TItem>, new()
{
TCol _values = default(TCol);
public TestEnum(IEnumerable<TItem> values)
{
_values = (TCol)(new TCol()).AddRange(values);
}
public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
public static implicit operator TItem(TestEnum<TCol, TItem> item)
{
return item._values.FirstOrDefault();
}
public static implicit operator TestEnum<TCol, TItem>(TCol values)
{
return new TestEnum<TCol, TItem>(values);
}
}
public static class EnumerableExtensions
{
public static IEnumerable<TItem> AddRange<TItem>(this IEnumerable<TItem> e, IEnumerable<TItem> values)
{
foreach (var cur in e)
{
yield return cur;
}
foreach (var cur in values)
{
yield return cur;
}
}
}
To repro the compile-time exception:
class Program
{
public static void Main()
{
TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
}
}
public class TestEnum<TCol, TItem>
where TCol : class, ICollection<TItem>, new()
{
TCol _values = default(TCol);
public TestEnum(IEnumerable<TItem> values)
{
_values = (TCol)(new TCol()).AddRange(values);
}
public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
public static implicit operator TItem(TestEnum<TCol, TItem> item)
{
return item._values.FirstOrDefault();
}
public static implicit operator TestEnum<TCol, TItem>(TCol values)
{
return new TestEnum<TCol, TItem>(values);
}
}
public static class EnumerableExtensions
{
public static TCol AddRange<TCol, TItem>(this TCol e, IEnumerable<TItem> values)
where TCol : IEnumerable<TItem>
{
foreach (var cur in e)
{
yield return cur;
}
foreach (var cur in values)
{
yield return cur;
}
}
}
Let's simplify:
static T M() where T : IEnumerable<int>
{
yield return 1;
}
Why is this illegal?
For the same reason that this is illegal:
static List<int> M()
{
yield return 1;
}
The compiler only knows how to turn M into a method that returns an IEnumerable<something>. It doesn't know how to turn M into a method that returns anything else.
Your generic type parameter T could be List<int> or any of infinitely many other types that implement IEnumerable<T>. The C# compiler doesn't know how to rewrite the method into one that returns a type it knows nothing about.
Now, regarding your method: what is the function of TCol in the first place? Why not just say:
public static IEnumerable<TItem> AddRange<TItem>(
this IEnumerable<TItem> s1, IEnumerable<TItem> s2)
{
foreach(TItem item in s1) yield return item;
foreach(TItem item in s2) yield return item;
}
Incidentally, this method already exists; it is called "Concat".
I'm not sure what are you trying to accomplish, your method certainly doesn't look like AddRange(), because it doesn't add anything to any collection.
But if you write an iterator block, it will return an IEnumerable<T> (or IEnumerator<T>). The actual run-time type it returns is compiler generated and there is no way to force it to return some specific collection, like List<T>.
From your example, AddRange() simply doesn't return List<T>, which is why you can't cast the result to that type. And there is no way to make iterator block return List<T>.
If you want to create a method that adds something to a collection, it probably means you need to call Add(), not return some other collection from the method:
public static void AddRange<T>(
this ICollection<T> collection, IEnumerable<T> items)
{
foreach (var item in items)
collection.Add(item);
}
In response to your comment to Eric Lippert's answer:
I hoped for a solution that would work against IEnumerable but will settle with one for ICollection.
public static void AddRange<TCol, TItem>(this TCol collection, IEnumerable<TItem> range)
where TCol : ICollection<TItem>
{
var list = collection as List<TItem>;
if (list != null)
{
list.AddRange(range);
return;
}
foreach (var item in range)
collection.Add(item);
}
I defined the method as void to mimic the semantics of List<T>.AddRange().

Categories