{ public System.Func

Why am I getting an exception "Cannot exclude object of type"?

I have the following code

public class SortTerm<T> { public System.Func<T, System.IComparable> Sort; public SortDirection Direction; public SortTerm(System.Func<T, System.IComparable> sorter, SortDirection direction) { this.Sort = sorter; this.Direction = direction; } public SortTerm(System.Func<T, System.IComparable> sorter) : this(sorter, SortDirection.Ascending) { } public static SortTerm<T> Create<TKey>(System.Func<T, TKey> sorter, SortDirection direction) where TKey : System.IComparable { return new SortTerm<T>((System.Func<T, System.IComparable>)(object)sorter, direction); } // End Constructor public static SortTerm<T> Create<TKey>(System.Func<T, TKey> sorter) where TKey : System.IComparable { return Create<TKey>(sorter, SortDirection.Ascending); } // End Constructor } 

Who needs to drop System.Func<T, TKey> to System.Func<T, IComparable>

Why

 SortTerm<Db.T_projects>.Create(x => x.name); 

a

 SortTerm<Db.T_projects>.Create(x => x.id); 

provides Invalid Cast

InvalidCastException: Unable to use an object of type "System.Func2 [Db.T_projects, System.Int64]" to enter 'System.Func2 [Db.T_projects, System.IComparable]'.

when long / int64 is defined as

 public struct Int64 : IComparable, IComparable<Int64>, IConvertible, IEquatable<Int64>, IFormattable 

While the string is not defined as IComparable ...

 public sealed class String : IEnumerable<char>, IEnumerable, IComparable, IComparable<String>, IConvertible, IEquatable<String>, ICloneable 

for completeness

 public partial class T_projects { public long id; // int not null public string name; // nvarchar(4000) not null } 

Doesn't that work?

And more importantly, how to do this?

Note:
There will be a List<SortTerm<T>>, so I cannot just use TKey in the sort definition.

+4
source share
2 answers

As RenΓ© Vogt already told you in the comments, this is because co-variant conversion is not supported for value types (like Int64), whereas it works for reference types (like string).

Fix this by putting the sorter on the delegate with the appropriate signature.
The order of the delegate type, so to speak:

 public class SortTerm<T> { public System.Func<T, System.IComparable> Sort; public SortDirection Direction; public SortTerm(System.Func<T, System.IComparable> sorter, SortDirection direction) { this.Sort = sorter; this.Direction = direction; } public SortTerm(System.Func<T, System.IComparable> sorter) : this(sorter, SortDirection.Ascending) { } public static SortTerm<T> Create<TKey>(System.Func<T, TKey> sorter, SortDirection direction) where TKey : System.IComparable { // return new SortTerm<T>((System.Func<T, System.IComparable>)(object)sorter, direction); return new SortTerm<T>(delegate (T x) { TKey ret = sorter(x); return (System.IComparable)ret; }, direction); } // End Constructor public static SortTerm<T> Create<TKey>(System.Func<T, TKey> sorter) where TKey : System.IComparable { return Create<TKey>(sorter, SortDirection.Ascending); } // End Constructor } 

You might want to abandon the where TKey : System.IComparable constraint, as this will not work for types with a null value, for example.

 SortTerm<Db.T_projects>.Create(x => x.created); 

for

 public partial class T_projects { public long id; // int not null public string name; // nvarchar(4000) not null public System.DateTime? created; // datetime null } 

like afaik there is no way

 where TKey : System.IComparable or System.Nullable<System.IComparable> 
0
source

Compared to docs, contra-variance only works for reference types - for example. string

Deviation applies only to reference types; if you specify a value type for a variant type parameter, this type parameter is invariant for the resulting constructed type.

So you can do the following:

 Func<string> myFunc = ... Func<object> func = myFunc; 

but not this:

 Func<int> myFunc = ... Func<object> func = myFunc; 

To work around this, set the id field to object :

 Func<object> = () => (object) id; 

which will look like this:

 SortTerm<Db.T_projects>.Create(x => (object) x.id); 

or create a new delegate from the existing one:

 Func<object> = () => myFunc(); 

which matches this in your code:

 SortTerm<Db.T_projects>.Create(new Func<MyType, object>(x => x.id)); 
0
source

Source: https://habr.com/ru/post/1275464/


All Articles