Now I have a SomeClass<T> with a constructor SomeClass(IList<T?> list). But when I use a List<int?> to construct it, the compiler told me:
Cannot resolve constructor
SomeClass(System.Collections.Generic.List<System.Nullable<int>>),
candidates are: SomeClass(System.Collections.Generic.IList<int>)
I find it means that I have to add "struct" to T's base class list to make sure that T is a value type, but why does this happen and how can make this class avoid using only value type as generic parameter?
#canton7 explains why this does not work in a comment.
You cannot solve this with a constructor, as you will have to introduce a new type parameter and constructors cannot declare type parameters. Therefore I suggest using a factory method.
public class SomeClass<T>
{
public IList<T> List { get; private set; }
public static SomeClass<T> Create<U>(IList<Nullable<U>> list)
where U : struct, T
{
return new SomeClass<T> {
List = list
.OfType<T>()
.ToList()
};
}
}
Introducing the type parameter U allows us to add a type constraint that is limited to this method, while the type parameter T of the class remains unconstrained.
Note that a null value does not have a type. Therefore, OfType<T>() filters out nulls.
You can test it like this:
var ints = new List<int?> { 1, 2, null, 3 };
var sut = SomeClass<int>.Create(ints);
foreach (int item in sut.List) {
Console.WriteLine(item);
}
Prints:
1
2
3
Related
public abstract class Flattenable<T_Id, T_Order> where T_Id : struct, IComparable
where T_Order : IComparable
{
public abstract T_Id ID { get; }
public abstract Nullable<T_Id> Parent_ID { get; }
public abstract T_Order Order { get; }
}
partial class MyObjectClass : Flattenable<int, int>
{
public override int ID => this.My_Id;
public override int? Parent_ID => this.My_ParentId;
public override int Order => this.My_Ordinal;
}
public class FlatTree<T, T_Id, T_Order> where T : Flattenable<T_Id, T_Order>
where T_Id : struct, IComparable
where T_Order : IComparable
{
public FlatTree(IEnumerable<T> list)
{}
//Code using Flattenable .ID, Parent_ID , Order
}
In this code, I have an abstract class Flattenable to extent an existing class with the necessery properties for my method to work.
My issue is that initialising my object I want the generik type to be some how "herited".
So One don't have to check MyObjectClass definition for the Flattenable generic type.
What I want var temp = new FlatTree<MyObjectClass>(myList);.
Usage :
List<MyObjectClass> myList = GetItems();
var temp = new FlatTree<MyObjectClass,int,int>(myList); // => work
// What I want
var temp = new FlatTree<MyObjectClass>(myList); // => Sub type get sniffed from MyObjectClass.
var temp = new FlatTree(myList); // Sub type get sniffed from Enumerable T
// What I want
var temp = new FlatTree(myList); // Sub type get sniffed from Enumerable T
Type inference does not work on constructors, because the constructor name is effectively the type of the object you're trying to instantiate.
The above syntax would be unable to resolve ambiguity if you codebase had existing class definitions for both FlatTree and any generic FlatTree<>; which supports the notion that constructors must explicitly specify all the generic types in order to identify the correct class.
// What I want
var temp = new FlatTree<MyObjectClass>(myList); // => Sub type get sniffed from MyObjectClass.
Even if you weren't dealing with constructors, partial type inference is currently not possible in C#. Type inference cannot complete a partial generic typing for you.
You have to specify the entire set of generic types to be used; or in cases where type inference works it means that you don't have to specify any generic type. There is no case where type inference only resolves some but not all of the generic types.
I've been baffled by this and can't seem to get my head around it so hopefully someone can point me in the right direction.
I have a class as follows:
public class Foo<T>
{
public List<T> Data;
}
Now I'm writing code to reflect this class and want to work out a way of determining that the field Data has a generic parameter being used.
My initial approach was to continue going down as many levels as I could and once I hit the IsGenericParameter field set to true I would rather than reflect the type name instead place a "Generic Argument" string there, however I can't seem to get this to work the way I want it to.
I've looked around but every solution I've found seems to point to a dead end with this at the moment.
You want IsGenericType, not IsGenericParameter:
bool isGeneric = typeof(Foo<int>).GetField("Data").FieldType.IsGenericType;
If you want to know of the parameter for the List is generic, then you have to look one more level down:
bool isGeneric = typeof(Foo<>).GetField("Data")
.FieldType
.GetGenericArguments()[0] // only generic argument to List<T>
.IsGenericParameter;
what if Data field was a Dictionary with Dictionary<string, T>. How would I determine which type was using a generic parameter?
Call GetGenericArguments on the type and look at each type in the resulting array
public class Foo<T>
{
public Dictionary<string, T> Bar;
}
Type[] types = typeof(Foo<>).GetField("Bar").FieldType.GetGenericArguments();
Console.WriteLine("{0}\n{1}",
types[0].IsGenericParameter, // false, type is string
types[1].IsGenericParameter // true, type is T
);
Basically, IsGenericParameter is used when looking at the generic parameters of a type to see if it is generic or if is has a type sepcified.
Here is how to distinguish generic types that rely on class type parameter from generic types that do not. Consider this example:
class Foo<T> {
public List<T> field1; // You want this field
public List<int> field2; // not this field
}
Start by getting generic type definition, and pulling its type arguments:
var g = typeof(Foo<string>).GetGenericTypeDefinition();
var a = g.GetGenericArguments();
This will give you an array with a single type that represents generic type parameter T. Now you can go through all fields, and search for that type among generic type arguments of field types, like this:
foreach (var f in g.GetFields()) {
var ft = f.FieldType;
if (!ft.IsGenericType) continue;
var da = ft.GetGenericArguments();
if (da.Any(xt => a.Contains(xt))) {
Console.WriteLine("Field {0} uses generic type parameter", f.Name);
} else {
Console.WriteLine("Field {0} does not use generic type parameter", f.Name);
}
}
This code produces the following output:
Field field1 uses generic type parameter
Field field2 does not use generic type parameter
I've been following a post regarding extension methods from this post:
public static IEnumerable<T> Distinct<T,TKey>(this IEnumerable<T> list, Func<T,TKey> lookup) where TKey : struct {
return list.Distinct(new StructEqualityComparer<T, TKey>(lookup));
}
class StructEqualityComparer<T,TKey> : IEqualityComparer<T> where TKey : struct {
Func<T, TKey> lookup;
public StructEqualityComparer(Func<T, TKey> lookup) {
this.lookup = lookup;
}
public bool Equals(T x, T y) {
return lookup(x).Equals(lookup(y));
}
public int GetHashCode(T obj) {
return lookup(obj).GetHashCode();
}
}
Could someone explain the purpose of the where TKey : struct appended to the extension method and comparator class. Removing these statements seems to make no difference to a simple test code - both evaluations TKey are of type int on a class and struct respectively:
public struct TestMeStruct
{
public int a;
public int b;
}
public class TestMeClass
{
public int a { get; set; }
public int b { get; set; }
}
public void Test()
{
List<TestMeStruct> lstruct = new List<TestMeStruct>();
lstruct.Add(new TestMeStruct() { a = 1, b = 2 });
lstruct.Add(new TestMeStruct() { a = 3, b = 7 });
lstruct.Add(new TestMeStruct() { a = 3, b = 14 });
lstruct.Add(new TestMeStruct() { a = 32, b = 11 });
List<TestMeClass> lclass = new List<TestMeClass>();
lclass.Add(new TestMeClass() { a = 1, b = 2 });
lclass.Add(new TestMeClass() { a = 3, b = 7 });
lclass.Add(new TestMeClass() { a = 3, b = 14 });
lclass.Add(new TestMeClass() { a = 32, b = 11 });
var one = lstruct.Distinct(mem => mem.a).ToList();
var two = lclass.Distinct(mem => mem.a).ToList();
}
Both return identical lists. Much obliged for any clarity on what's going on!
From msdn
The where clause is used to specify constraints on the types that can
be used as arguments for a type parameter defined in a generic
declaration. For example, you can declare a generic class,
MyGenericClass, such that the type parameter T implements the
IComparable interface:
public class MyGenericClass where T:IComparable { }
I believe you know what is generic type constraints and especially where : struct, in short -by specifying such constraint you indicate that only value types could be used as generic type parameter, for instance int, double, etc.
In current implementation of Distinct and StructEqualityComparer this really does not make any difference, but idea of StructEqualityComparer is to compare structs, its name saying this, so all classes/method which are passing through own generic type parameters required for StructEqualityComparer obligated redefine the same generic type constraints as well. In your case Distinct() method passign its ownt T parameter so obligated to redefine all constraints.
The where keyword is used to qualify a generic parameter. In this case, you are indicating that the StructEqualityComparer must take something that is a value type (or struct), so that an equality comparison will compare by value rather than by reference.
I am pretty sure you are not asking what 'where' means. You are asking 'what is the point in putting it there'.
In the original post that you linked to, Sam Saffron said "A similar helper class can be built to compare objects. (It will need to do better null handling)". So, with your little test suite nothing goes wrong, because you are not passing any nulls. Try passing nulls, and it will blow up.
What probably happened is that Sam Saffron wrote an EqualityComparer, and then realized that he should be checking for nulls all over the place, which would make his code kind of unsuitable as an example, so instead of adding null checks he just renamed it to StructEqualityComparer and he made it only work with structs.
Here are my classes definitions:
public abstract class AbstractEntity : ...
public partial class AbstractContactEntity : AbstractEntity, ...
public sealed class EntityCollectionProxy<T> : IList<T>, System.Collections.IList
where T : AbstractEntity
Now I get an object from a delegate and I want to cast it, and it doesn't work as I expect it to.
var obj = resolver.DynamicInvoke (this.entity);
var col = obj as EntityCollectionProxy<AbstractEntity>;
obj is of type EntityCollectionProxy<AbstractContactEntity>.
But col is null.
If I try the regular casting (var col = (Entity...) obj) I get an exception.
I would expect that it work since the types are coherent.
What do I miss?
They are not the same types. It is the same as with List<string> and List<int>: They also can't be casted to one another. That AbstractContactEntity is a AbstractEntity doesn't change this.
Extracting an interface from EntityCollectionProxy<T> and making it covariant doesn't work either, because you want to implement IList<T> which means you have input paramaters and return values of type T which prevents covariance.
The only possible solution is the following:
var tmp = (EntityCollectionProxy<AbstractContactEntity>)obj;
var col = tmp.Select(x => (AbstractEntity)x);
col will be of type IEnumerable<AbstractEntity>. If you want to have a EntityCollectionProxy<AbstractEntity>, you need to create a new one:
var result = new EntityCollectionProxy<AbstractEntity>(col);
This assumes that your EntityCollectionProxy<T> class has a constructor that accepts an IEnumerable<T>.
But beware, this will be a NEW instance and not the same as returned by resolver.DynamikInvoke.
I cannot find a way to pass an anonymous type to a generic class as a type parameter.
// this is the class I want to instantiate
public class ExtArrayStore<T> : IViewComponent
{
public IQueryable<T> Data { get; set; }
...
// the creator class
public static class ArrayStoreGenerator
{
public static ExtArrayStore<T> CreateInstance<T>(IQueryable<T> query)
{
return new ExtArrayStore<T>();
}
}
// trying to use this
IQueryable usersQuery= ((from k in bo.usersselect new { userid = k.userid, k.username}).AsQueryable());
var x = ArrayStoreGenerator.CreateInstance(usersQuery);
I am getting;
The type arguments for method ArrayStoreGenerator.CreateInstance(System.Linq.IQueryable)' cannot be inferred from the usage. Try specifying the type arguments explicitly
Is there a way to achieve this? ( I am thinking of Interfaces and returning an interface, but not sure if it would work) can any one help with passing anon types to generics.
usersQuery is being typed as the non-generic IQueryable because you explicitly specify that in the variable's declaration.
Instead, do var usersQuery = .... This will type the variable as IQueryable<TAnon>, which then matches the signature of ArrayStoreGenerator.CreateInstance.
You should define usersQuery as var.
What if you try var usersQuery local variable instead of explicitly specify its type?
Try with ToArray:
var x = ArrayStoreGenerator.CreateInstance(usersQuery.ToArray());