Simplify generic type declaration - c#

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.

Related

About Nullable of C# generic parameter

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

No implicit reference conversion from 'List<QSO>' to 'List<List<QSO>>'

I am trying to create a generic class that accepts only a List<T>
public class MyTables<T> where T : List<T>
{
public MyTables(T list)
{
}
}
When trying to instantiate it the compiler complains
var mt = new MyTables<List<QSO>>(Log.QSOs);
// Log.QSOs is an object property defined as
// public List<QSO> QSOs { get; set; }
This is the error:
Error CS0311 The type 'List<QSO>' cannot be used as type parameter 'T'
in the generic type or method 'MyTables<T>'.
There is no implicit reference conversion from
'List<QSO>' to 'List<List<QSO>>'
Why is it expecting a 'List<List<QSO>>' instead of simply 'List<QSO>'?
The generic parameter is T, which you have constrained to List<T>. Whatever you provide as the generic parameter must be a list of itself, which is presumably not what you want!
Here is a type that satisfies your current constraint:
class MyTable : List<MyTable>
{ }
You probably instead want to define the class like this:
public class MyTables<T>
{
public MyTables(List<T> list)
{
}
}

Storing type in variable to declare properties

To create a generic base class I want to have the type of my fields stored in variables. Something like this (THIS is just not working):
public class MyClass
{
public MyClass()
{
myType = typeof(string);
}
public Type myType { get; set; }
public myType MyProperty { get; set; }
}
I handle lots of models and ObservableCollections of models from a database with more than 70 tables that I get via Entity Framework Core and I don't want to copy/paste (DRY!) the business logic for every table just because of the type of changes.
Is there another approach to what I came up with?
As you've seen, what you are trying to do is simply not possible. A variable of type Type cannot be used to declare another variable. (The same applies for fields, properties, etc). What you are looking for is called Generics, specifically generic classes. Here is what your code would look like with a generic class:
public class MyClass<T>
{
public T MyProperty { get; set; }
public Type TypeOfT => typeof(T); // for demo
}
In the above example, T (called the generic type parameter) is a sort of placeholder. It can be replaced with any* class/struct/interface. This is done when you create a new instance of the type:
var objInt = new MyClass<int>();
Console.WriteLine(objInt.TypeOfT.Name); // System.Int32
objInt.MyProperty = 5;
var objString = new MyClass<string>();
Console.WriteLine(objString.TypeOfT.Name); // System.String
objString.MyProperty = "Hello!";
They may even be used with other generic types:
var obj = new MyClass<MyClass<string>>();
obj.MyProperty = new MyClass<string>();
obj.MyProperty.MyProperty = "World";
Console.WriteLine(obj.TypeOfT.Name); // MyClass`1
Console.WriteLine(obj.MyProperty.TypeOfT.Name); // System.String
The type parameter remains consistent thoroughout the entire class. It can appear in properties, fields, return types and method parameters. A class can have multiple type parameters as well. Additionally, methods may specify generic arguments with or without their containing class being generic themselves.
I suggest following up with reading the docs pointed to by the above links. Generics is quite a big topic--one that cannot be done justice here.
* There are some types that cannot be used as generic arguments, for example ref structs

how to cast an object to a type passed as a parameter

Following this question, I see it's possible to pass a type to a method. Inside the method to which the type has been passed, how would an object be cast to that passed type? As a complication, class Foo inherits from a class which I cannot change.
var x = FetchData();
Foo foo = new Foo(2, typeof(Gizmo)); // pass the Gizmo type
foo.Execute(x);
public class Foo : ThirdPartyLibrary.Operation
{
Type customtype;
public Foo(int i, Type passedtype) : base()
{
this.customtype=passedtype;
}
public override void Execute(ThirdPartyLibrary.Node node)
{
var record = ( ??? ) node.GetData(); // cast node to the type of customtype
}
}
If I'm understanding your question correctly, you can do this with generics. It would look something like this (based off your example code):
public class Foo<T> : ThirdPartyLibrary.Operation
{
public Foo(int i) : base()
{
//hopefully you actually do something useful with "i" here.
}
public override void Execute(ThirdPartyLibrary.Node node)
{
//I'm not 100% sure which object you are trying to cast, so I'm showing both forms below. You obviously won't be able to do both without changing the variable name.
//If you want to cast the "Data", use this.
var record = (T) node.GetData();
//If you want to cast "node", use this.
var record = ((T) node).GetData();
}
}
You use it this way:
var x = FetchData();
Foo foo = new Foo<Gizmo>(2);
foo.Execute(x);
customtype is no longer required, as you can access to the Type of T with typeof(T) from anywhere within the class.

Dilemma in calling constructor of generic class

I have this generic singleton that looks like this:
public class Cache<T>
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
}
public T LoadFromSharePoint(Guid id)
{
return new T(id) // Here is the problem.
}
}
The error message is:
Cannot create an instance of type T because it does not have the new() constraint.
I have to mention that I must pass that id parameter, and there is no other way to do so. Any ideas on how to solve this would be highly appreciated.
Normally you would constrain the type T to a type that has a default constructor and call that. Then you'd have to add a method or property to be able to provide the value of id to the instance.
public static T LoadFromSharePoint<T>(Guid id)
where T : new() // <-- Constrain to types with a default constructor
{
T value = new T();
value.ID = id;
return value;
}
Alternatively since you specify that you have to provide the id parameter through the constructor, you can invoke a parameterized constructor using reflection. You must be sure the type defines the constructor you want to invoke. You cannot constrain the generic type T to types that have a particular constructor other than the default constructor. (E.g. where T : new(Guid) does not work.)
For example, I know there is a constructor new List<string>(int capacity) on List<T>, which can be invoked like this:
var type = typeof(List<String>);
object list = Activator.CreateInstance(type, /* capacity */ 20);
Of course, you might want to do some casting (to T) afterwards.
To do this you should specify what T is. Your Cache<T> can hold anything? Tiger, Fridge and int as well? That is not a sound design. You should constrain it. You need an instance of T which will take a Guid to construct the instance. That's not a generic T. Its a very specific T. Change your code to:
public class Cache<T> where T : Cacheable, new()
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
//you're first checking for presence, and then adding to it
//which does the same checking again, and then returns the
//value of key again which will have to see for it again.
//Instead if its ok you can directly return
//return cachedBlocks[id] = LoadFromSharePoint(id);
//if your LoadFromSharePoint is not that expensive.
//mind you this is little different from your original
//approach as to what it does.
}
public T LoadFromSharePoint(Guid id)
{
return new T { Key = id }; // Here is no more problem.
}
}
public interface Cacheable
{
Guid Key { get; set; }
}
Now derive all the cacheables (whatever Ts that you will pass it for Cache<T>) from the interface Cacheable.
In order to use the constructor of a Generic Type without any constraint, and within the class, the syntax where T : class, new() needs to be used
This enables to change values of attributes (fields) - not only get/set properties) at runtime depending the target class used
First, declaring the generic class:
public class Foo<T> where T : class, new()
{
public T oneEmptyElement()
{
return new T();
}
public T setAttribute(string attributeName, string attributeValue)
{
T objT = new T();
System.Reflection.FieldInfo fld = typeof(T).GetField(attributeName);
if (fld != null)
{
fld.SetValue(objT, attributeValue);
}
return objT;
}
public List<T> listOfTwoEmptyElements()
{
List<T> aList = new List<T>();
aList.Add(new T());
aList.Add(new T());
return aList;
}
}
Declare then a potential target class:
public class Book
{
public int name;
}
And finally the call can be done like this:
Foo<Book> fooObj = new Foo<Book>();
Book aBook = fooObj.oneEmptyElement();
aBook.name = "Emma";
Book anotherBook = fooObj.setAttribute("name", "John");
List<Book> aListOfBooks = fooObj.listOfTwoEmptyElements();
aListOfBooks[0].name = "Mike";
aListOfBooks[1].name = "Angelina";
Console.WriteLine(aBook.name); //Output Emma
Console.WriteLine(anotherBook.name); //Output John
Console.WriteLine(aListOfBooks[0].name); // Output Mike
Console.WriteLine(aListOfBooks[1].name); // Output Angelina

Categories