Generic interface inheritance castings - c#

Currently, I'm facing up with a generic interfaces inheritance problem:
interface IParent<T> {
R Accept<R>(IParentVisitor<T, R> visitor);
}
interface IChild<T, TKey> : IParent<T> {
R Accept<R>(IChildVisitor<T, TKey, R> visitor);
}
I've also created these Visitor interfaces:
interface IParentVisitor<T, R> {
R Visit(IParent<T> parent);
}
interface IChildVisitor<T, TKey, R> : IParentVisitor<T, R> {
R Visit(IChild<T, TKey> viz);
}
Implementations:
class ParentVisitor<T> : IParentVisitor<T, MyResultClass> {
MyResultClass Visit(IParent<T> parent) {
return //...;
}
}
class ChildVisitor<T, TKey> : IChildVisitor<T, TKey, MyAnotherResultClass> {
MyAnotherResultClass Visit(IChild<T, TKey> child) {
return //...;
}
}
Up to now, everything seems to be right. However, guess this situation:
static void Main() {
IList<IParent<MyT>> interfaces = new List<IParent<MyT>>();
interfaces.add(new Parent<MyT>());
interfaces.add(new Child<MyT, string>());
interfaces.add(new Child<MyT, int>());
interfaces.add(new Child<MyT, TimeSpan>());
//...
//(!!*!!)
foreach (IChild<MyT, string> stringChild in interfaces.OfType<IChild<MyT, string>()) {
ChildVisitor stringChildVisitor = new ChildVisitor<MyT, string>();
var result = stringChild.Accept(stringChildVisitor);
//...
}
//(!!*!!)
foreach (IChild<MyT, int> intChild in interfaces.OfType<IChild<MyT, int>()) {
ChildVisitor intChildVisitor = new ChildVisitor<MyT, int>();
var result = stringChild.Accept(intChildVisitor);
//...
}
//and so on up to infitiny types in C#...
}
I think it's a non-viable way to get what I want.
What I want to get is to use a ChildVisitor on whichever IChild<T, *>, where * is whichever type. I don't know if I've explained so well.
Something like that:
//(!!*!!)
foreach (IChild<MyT, *> asterisckChild in interfaces.OfType<IChild<MyT, *>()) {
ChildVisitor asterisckChildVisitor = new ChildVisitor<MyT, *>();
var result = asterisckChild.Accept(asterisckChildVisitor);
//...
}
Any ideas?

IList list = new ArrayList();
list.Add(new Dictionary<string, string>
{
["key"] = "value"
});
list.Add(new Dictionary<string, int>()
{
["key"] = 123
});
list.Add("just for the test");
foreach(var it in list)
{
foreach(var face in it.GetType().GetInterfaces())
{
//1. Filter out non-generic interfaces
// (following GetGenericTypeDefinition() would throw exception otherwise)
if (!face.IsGenericType)
continue;
//2. Filter out anything that is not IDictionary<,>
if (typeof(IDictionary<,>) != face.GetGenericTypeDefinition())
continue;
//3. Filter out anything that is not IDictionary<string,>
var generic = face.GetGenericArguments();
if (typeof(string) != generic[0]) //note: consider IsAssignableFrom instead
continue;
//4. Invoke the method
var valueType = generic[1];
var method = face.GetMethod("TryGetValue"); //note: needs to be more specific if overloaded
//don't know how you want to choose R for the Accept<R>
//method = method.MakeGenericMethod(new[] { typeof(R) });
var arguments = new object[] { "key", null };
method.Invoke(it, arguments);
var value = arguments[1];
//5. Do something with the result
Console.WriteLine(value.ToString());
}
}

Related

Creating a Generic Function to convert an Array into a Dictionary in C#

I have a struct which contains two public variables. I have made an array of that struct, and wish to convert it to a Dictionary.
Here is one such method of accomplishing that:
public class TestClass
{
public struct KeyValuePairs
{
public string variableOne;
public float variableTwo
}
private KeyValuePairs[] keyValuePairs;
private Dictionary<string, float> KeyValuePairsToDictionary()
{
Dictionary<string, float> dictionary = new Dictionary<string, float>();
for(int i = 0; i < keyValuePairs.Length; i++)
{
dictionary.Add(keyValuePairs[i].variableOne, keyValuePairs[i].variableTwo);
}
return dictionary;
}
}
Now, that works for my specific setup, but I wish to try and convert the KeyValuePairsToDictionary() function into a Generic so that it may work across all types.
My first thought, then, was to do something like this:
private Dictionary<T, T> ArrayToDictionary<T>(T[] array)
{
Dictionary<T, T> keyValuePairs = new Dictionary<T, T>();
for(int i = 0; i < array.Length; i++)
{
keyValuePairs.Add(array[i], array[i]); //The problem is right here.
}
return keyValuePairs;
}
As you can probably tell, I can't access the public fields of whatever struct array I am trying to convert into key-value pairs.
With that, how would you suggest I go about performing the generic conversion?
Please note that my specific setup requires that I convert a struct to a dictionary, for I am using the Unity Game Engine.
Thank you.
A generic way of doing this is already implemented in LINQ.
var dict = myArray.ToDictionary(a => a.TheKey);
With your implementation
public struct KeyValuePairs
{
public string variableOne;
public float variableTwo;
}
and an array
KeyValuePairs[] keyValuePairs = ...;
You get
Dictionary<string, KeyValuePairs> dict = keyValuePairs
.ToDictionary(a => a.variableOne);
or alternatively
Dictionary<string, float> dict = keyValuePairs
.ToDictionary(a => a.variableOne, a => a.variableTwo);
Note that the first variant yields a dictionary with values of type KeyValuePairs, while the second one yields values of type float.
According to the conversation, it seems that you are interested on how you would implement this. Here is a suggestion:
public static Dictionary<TKey, TValue> ToDictionary<T, TKey, TValue>(
this IEnumerable<T> source,
Func<T, TKey> getKey,
Func<T, TValue> getValue)
{
var dict = new Dictionary<TKey, TValue>();
foreach (T item in source) {
dict.Add(getKey(item), getValue(item));
}
return dict;
}
Or simply like this, if you want to store the item itself as value
public static Dictionary<TKey, T> ToDictionary<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> getKey
{
var dict = new Dictionary<TKey, T>();
foreach (T item in source) {
dict.Add(getKey(item), item);
}
return dict;
}
You can use Reflection to achieve that
First of all, add a {get;set;} to the variables to transform them in properties
public struct KeyValuePairs
{
public string variableOne { get; set; }
public float variableTwo { get; set; }
}
Then the method
// T1 -> Type of variableOne
// T2 -> Type of variableTwo
// T3 -> KeyValuesPair type
public static Dictionary<T1, T2> convert<T1,T2,T3>(T3[] data)
{
// Instantiate dictionary to return
Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
// Run through array
for (var i = 0;i < data.Length;i++)
{
// Get 'key' value via Reflection to variableOne
var key = data[i].GetType().GetProperty("variableOne").GetValue(data[i], null);
// Get 'value' value via Reflection to variableTow
var value = data[i].GetType().GetProperty("variableTwo").GetValue(data[i], null);
// Add 'key' and 'value' to dictionary casting to properly type
dict.Add((T1)key, (T2)value);
}
//return dictionary
return dict;
}
I used the following code to test
KeyValuePairs[] val = new KeyValuePairs[5];
val[0] = new KeyValuePairs() { variableOne = "a", variableTwo = 2.4f };
val[1] = new KeyValuePairs() { variableOne = "b", variableTwo = 3.5f };
val[2] = new KeyValuePairs() { variableOne = "c", variableTwo = 4.6f };
val[3] = new KeyValuePairs() { variableOne = "d", variableTwo = 5.7f };
val[4] = new KeyValuePairs() { variableOne = "e", variableTwo = 6.8f };
Dictionary<string, float> dict = convert<string, float,KeyValuePairs>(val);

Arithmetic operation like Add for a Generic List<T>

Doing Add for two elements like T a, T b is simple, Mark has provided a good solution using Expression tree here, which translates into following and is simple to use:
static T Add<T>(T a, T b)
{
// Declare Parameter Expressions
ParameterExpression paramA = Expression.Parameter(typeof(T), "valueA"),
paramB = Expression.Parameter(typeof(T), "valueB");
// add the parameters together
BinaryExpression body = Expression.Add(paramA, paramB);
// Compile it
Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
// Call it
return add(a, b);
}
Challenge that I have is there's a collection of List<T>, where all elements have to added as shown above. I have tried following, on same lines as above, but it doesn't work:
static T AddAll<T>(List<T> list)
{
var parameterExpressionList = list.Select((x,i) => (Expression)Expression.Parameter(typeof(T), "value"+i));
var body = parameterExpressionList
.Skip(1)
.Aggregate(parameterExpressionList.First(),
(paramA, paramB) => Expression.Add(paramA, paramB));
// Compile it
Func<List<T>, T> addAll = Expression.Lambda<Func<List<T>, T>>(body, parameterExpressionList.Cast<ParameterExpression>()).Compile();
return addAll(list);
}
Run-time error that I get is: Incorrect number of parameters supplied for lambda declaration. Any pointer, how to achieve, please note, I don't need a solution where I cumulatively pick two elements from actual list and call Add<T>(T a, T b) , since that would lead to multiple times compilation of the Expression tree, which is not efficient, as I would have > 100 K data points, any suggestion to make my code work would be great, I am not sure where it is going wrong.
Since you have created a generic function, just use it on the list (I added an optional Adder method to handle non-standard classes):
static T AddAll<T>(IEnumerable<T> src, Func<T, T, T> adder = null) {
// Declare Parameter Expressions
ParameterExpression paramA = Expression.Parameter(typeof(T), "valueA"),
paramB = Expression.Parameter(typeof(T), "valueB");
// add the parameters together
BinaryExpression body;
if (adder == null)
body = Expression.Add(paramA, paramB);
else
body = Expression.Add(paramA, paramB, adder.GetMethodInfo());
// Compile it
Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
// Call it
return src.Aggregate(default(T), (ans, n) => add(ans, n));
}
You can use the Adder parameter to handle things like strings:
var ans = AddAll(new[] { "a", "b", "c" }, String.Concat);
Since we know the type of T at compile time though, we can just call Sum:
static T AddAll2<T>(IEnumerable<T> src) {
var paramA = Expression.Parameter(typeof(IEnumerable<T>), "valueA");
var method = typeof(Enumerable).GetMethod("Sum", new[] { typeof(IEnumerable<T>) });
if (method != null) {
// Create lambda body
var body = Expression.Call(method, paramA);
// Compile it
Func<IEnumerable<T>, T> sum = Expression.Lambda<Func<IEnumerable<T>, T>>(body, paramA).Compile();
// Call it
return sum(src);
}
else
return default(T);
}
Of course, if you are going to call Sum, you don't need a lambda:
static T AddAll3<T>(IEnumerable<T> src) {
var method = typeof(Enumerable).GetMethod("Sum", new[] { typeof(IEnumerable<T>) });
if (method != null) {
// Call it
return (T)method.Invoke(null, new[] { src });
}
else
return default(T);
}
Just try to get every item from your list and then accumulate they into result.
static T AddAll<T>(List<T> list)
{
if (list.Count == 0)
{
// It's additional small case
return default(T);
}
var listParam = Expression.Parameter(typeof(List<T>));
var propInfo = typeof(List<T>).GetProperty("Item");
var indexes = list.Select((x, i) => Expression.MakeIndex(listParam, propInfo, new[] { Expression.Constant(i) }));
Expression sum = indexes.First();
foreach (var item in indexes.Skip(1))
{
sum = Expression.Add(sum, item);
}
var lambda = Expression.Lambda<Func<List<T>, T>>(sum, listParam).Compile();
return lambda(list);
}
You can pass list directly as argument and just create sum via indexes:
static T AddAll<T>(List<T> list)
{
if (list.Count == 0) return default(T);
if (list.Count == 1) return list[0];
var indexerProperty = typeof(List<T>).GetProperty("Item");
var p = Expression.Parameter(typeof(List<T>));
var exp = Expression.Add(
Expression.MakeIndex(p, indexerProperty, new [] { Expression.Constant(0) }),
Expression.MakeIndex(p, indexerProperty, new [] { Expression.Constant(1) }));
for (var i = 2; i < list.Count; i++)
{
exp = Expression.Add(
exp,
Expression.MakeIndex(p, indexerProperty, new [] { Expression.Constant(i) }));
}
var lambda = Expression.Lambda<Func<List<T>, T>>(exp, p).Compile();
return lambda(list);
}
Store all applicable Enumerable.Sum overloads in a dictionary:
// all methods with signature public static T Enumerable.Sum(IEnumerable<T>) by element type
private static readonly Dictionary<Type, MethodInfo> _sumMethodsByElementType = typeof(Enumerable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "Sum" && !m.IsGenericMethod)
.Select(m => new { Method = m, Parameters = m.GetParameters() })
.Where(mp => mp.Parameters.Length == 1)
.Select(mp => new { mp.Method, mp.Parameters[0].ParameterType })
.Where(mp => mp.ParameterType.IsGenericType && mp.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(mp => new { mp.Method, ElementType = mp.ParameterType.GetGenericArguments()[0] })
.Where(me => me.Method.ReturnType == me.ElementType)
.ToDictionary(mp => mp.ElementType, mp => mp.Method);
Invoke the corresponding one from inside the generic AddAll (or Sum as I prefer to call it) method:
public static T Sum<T>(IEnumerable<T> summands)
{
MethodInfo sumMethod;
if (!_sumMethodsByElementType.TryGetValue(typeof(T), out sumMethod)) throw new InvalidOperationException($"Cannot sum elements of type {typeof(T)}.");
return (T)sumMethod.Invoke(null, new object[] { summands });
}
Test:
Console.WriteLine(Sum(new[] { 1, 2, 3 }));
Console.WriteLine(Sum(new[] { 1, 2, 3, default(int?) }));
Console.WriteLine(Sum(new[] { 1.1, 2.2, 3.3 }));
Console.WriteLine(Sum(new[] { 1.1, 2.2, 3.3, default(double?) }));
try { Console.WriteLine(Sum(new[] { 'a', 'b', 'c' })); }
catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); }
Output:
6
6
6.6
6.6
Cannot sum elements of type System.Char.
You don't necessarily need to solve every part of the problem with expressions, if you are just interested in the operation itself
Here's an implementation that uses Singleton via the Lazy<> type for the default addition of type T (instead of static methods)
The LinqExpression expression could potentially be reused if you really need expressions (say, in an EF scenario), but there's no equivalent expression for the AddAll operation... though it can potentially be expanded to support a generic expression for AddAll
public abstract class Addition<T>
{
private readonly Lazy<Expression<Func<T, T, T>>> _lazyExpression;
private readonly Lazy<Func<T, T, T>> _lazyFunc;
public Func<T, T, T> Execute
{
get { return _lazyFunc.Value; }
}
public Expression<Func<T, T, T>> LinqExpression
{
get { return _lazyExpression.Value; }
}
protected Addition()
{
_lazyExpression = new Lazy<Expression<Func<T, T, T>>>(InitializeExpression);
_lazyFunc = new Lazy<Func<T, T, T>>(() => LinqExpression.Compile());
}
protected abstract Expression<Func<T, T, T>> InitializeExpression();
}
public sealed class DefaultAddition<T> : Addition<T>
{
private static readonly Lazy<DefaultAddition<T>> _lazyInstance = new Lazy<DefaultAddition<T>>(() => new DefaultAddition<T>());
public static DefaultAddition<T> Instance
{
get {return _lazyInstance.Value; }
}
// Private constructor, you only get an instance via the Instance static property
private DefaultAddition()
{
}
protected override Expression<Func<T, T, T>> InitializeExpression()
{
var paramX = Expression.Parameter(typeof(T), "x");
var paramY = Expression.Parameter(typeof(T), "y");
var body = Expression.Add(paramX, paramY);
return Expression.Lambda<Func<T, T, T>>(body, paramX, paramY);
}
}
public static class Operations
{
public static T Add<T>(T x, T y)
{
return DefaultAddition<T>.Instance.Execute(x, y);
}
public static T AddAll<T>(IEnumerable<T> enumerable)
{
var itemAdd = DefaultAddition<T>.Instance.Execute;
return enumerable.Aggregate(default(T), (result, item) => itemAdd(result, item));
// This might be more efficient than Aggregate, but I didn't benchmark it
/*
var result = default(T);
foreach (var item in enumerable)
{
result = itemAdd(result, item);
}
return result;
*/
}
}
Usage:
// Can mix double with int :)
var doubleAdd = Operations.Add(4.5, 3);
// Can mix decimal with int :)
var listAdd = Operations.AddAll(new[] {3, 6.7m, 0.3m});
// Even empty enumerables
var shortAdd = Operations.AddAll(Enumerable.Empty<short>());
// This will not work for byte. System.Byte should be casted to System.Int32
// Throws "InvalidOperationException: The binary operator Add is not defined for the types 'System.Byte' and 'System.Byte'."
var byteAdd = Operations.AddAll(new byte[] {1, 2, 3});
If your T is of value type like int, long, double etc. then you can simply do this:
//add
//using System.Linq;
var items = new List<int>();
items.Add(1);
items.Add(5);
items.Add(10);
var sum = items.Sum();

Write similar logic using generics

I have the following method which determines which cars I need to delete from the DB.
private List<CarDTO> BuildCarsToDelete(IList<CarDTO> newCars, IList<CarDTO> existingCars)
{
var missingCars = new List<CarDTO>();
var cars = newCars.Select(c => c.CarId);
var newCarIds = new HashSet<int>(cars);
foreach (var car in existingCars)
{
//If there are no new cars then it had some and they have been removed
if (newCars.Count() == 0)
{
missingCars.Add(car);
}
else
{
if (!newCarIds.Contains(car.CarId))
{
missingCars.Add(car);
}
}
}
return missingCars;
}
This works as I want - but if I want to achieve the same functionality for Customers or Apartments of other DTOs I will be copying a pasting the code but only changing the variable names and the Type of DTO around - is there a nicer way possible using generics which would keep the algorithm and logic as it is but allow me to use on any DTO?
If all the ids are of type int then you can do that by passing in a Func to determine the id.
private List<T> BuildToDelete<T>(
IList<T> newItems,
IList<T> existingItems,
Func<T, int> getId)
{
var missingItems = new List<T>();
var items = newItems.Select(getId);
var newItemIds = new HashSet<int>(items);
foreach (var item in existingItems)
{
if (newItems.Count() == 0)
{
missingItems.Add(item);
}
else
{
if (!newItemIds.Contains(getId(item)))
{
missingItems.Add(item);
}
}
}
return missingItems;
}
Then call as shown below:
var results = BuildToDelete(newCars, existingCars, c => c.CarId);
Assuming you use the interface approach mentioned in comments, a generic version could look something like this:
private List<TEntity> BuildEntitiesToDelete(IList<TEntity> newEntities, IList<TEntity> existingEntities) where TEntity : IEntityWithId
{
var missingEntities = new List<TEntity>();
var entities = newEntities.Select(e => e.Id);
var newEntityIds = new HashSet<int>(entities);
foreach (var entity in existingEntities)
{
if (entities.Count() == 0)
{
missingEntities.Add(entity);
}
else
{
if (!newEntityIds.Contains(entity.Id))
{
missingEntities.Add(entity);
}
}
}
return missingEntities;
}
IEntityWithId is probably a poor name for the interface, but I'll leave picking a better name up to you.
Try something cleaner:
1) create flexible equality comparer (need to add some null checking etc.)
public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> comparer;
Func<T, int> hash;
public FuncEqualityComparer (Func<T, T, bool> comparer, Func<T, int> hash)
{
this.comparer = comparer;
this.hash = hash;
}
public bool Equals (T x, T y) => comparer (x, y);
public int GetHashCode (T obj) => hash (obj);
}
2) and now, just simply:
var carComparerByID = new FuncEqualityComparer<CarDTO> ((a, b) => a.CarId == b.CarId, x => x.CarId.GetHashCode ());
var result = existingCars.Except (newCars, carComparerByID).ToList ();

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).

Returning List<T> with different contents depending on T

I want to do something like this:
public List<T> GetList<T>()
{
if (typeof(T) == typeof(Type1))
{
return new List<Type1>() { new Type1(), new Type1(), new Type1() };
}
if (typeof(T) == typeof(Type2))
{
return new List<Type2>() {new Type2(), new Type2()};
}
throw new Exception("Unknown T");
}
public void DoStuffWithGenericList<T>()
{
var list = GetList<T>();
// do stuff that does not depend on T
}
But that, of course, is not legal. I feel I am missing something basic here :)
In my case, I am getting lists of different types of objects from Entity Framework, but the rest of my logic does not depend on the actual type. It can just work on List or it could be generic.
All Ts that GetList() will be called with as type parameter will inherit from the same base class, if it makes a difference.
Why not use the 'new' operator to instantiate the types:
public List<T> GetList<T>() where T : new()
{
if (typeof(T) == typeof(Type1))
{
return new List<T>() { new T() };
}
// etc...
throw new Exception("Unknown T");
}
All you have to do is ensure your types can be instantiated by adding the new() constraint.
Code like this cannot work because it depends on runtime type checks (you have written them explicitly). But how can the compiler know at compile time that the result of your runtime checks will be something that is actually a List<T>?
In this specific example case, you could achieve the desired aim with
public List<T> GetList<T>() where T : new()
{
if (typeof(T) == typeof(Type1))
{
return new List<T>() { new T(), new T(), new T() };
}
if (typeof(T) == typeof(Type2))
{
return new List<T>() { new T(), new T() };
}
throw new Exception("Unknown T");
}
But of course this does not solve any practical problem. If you have any specific question other than "why does this not work" in mind, you should edit the question to present it.
Consider this: to use GetList at some point in your code you will need to write
var x = GetList<SomeType>();
The type parameter SomeType must be hardcoded at the call site, otherwise the program will not compile. But if it must be hardcoded, then the above is not at all different from
public List<SomeType> GetListOfSomeType()
{
return new List<SomeType>();
}
var x = GetListOfSomeType();
So what are you trying to accomplish exactly?
Of course this counter-example is somewhat superficial, and in reality the generic version of GetList would allow increased flexibility if you are willing to use reflection. But again, in your example that is not the case.
public List<T> GetList<T>()
{
if (typeof(T) == typeof(Type1))
{
return new List<Type1>() { new Type1(), new Type1(), new Type1() }.Cast<T>().ToList();
}
if (typeof(T) == typeof(Type2))
{
return new List<Type2>() {new Type2(), new Type2()}.Cast<T>().ToList();
}
throw new Exception("Unknown T");
}
Just cast the return value since you already checked to make sure the type is right:
return (List<T>)(object)new List<Type1>(...
Whenever I'm going to see a if(typeof(T) == typeof(SomeType) i will switch to a dictionary which looks more or less this way:
public static class ListCreator
{
private static readonly Dictionary<Type, Func<object>> _Creators;
static ListCreator()
{
_Creators = new Dictionary<Type, Func<object>>();
InitializeDefaultCreators();
}
public static List<T> Create<T>()
{
Func<object> creator;
if (!_Creators.TryGetValue(typeof(T), out creator))
{
throw new InvalidOperationException("No creator available for type " + typeof(T).FullName);
}
return (List<T>)creator();
}
public static void Register<T>(Func<List<T>> creator)
{
_Creators.Add(typeof(T), creator);
}
public static void Register(Type type, Func<object> creator)
{
_Creators.Add(type, creator);
}
public static bool Unregister<T>()
{
return _Creators.Remove(typeof(T));
}
public static bool Unregister(Type type)
{
return _Creators.Remove(type);
}
private static void InitializeDefaultCreators()
{
Register(MyDoubleListCreator);
Register(typeof(int), () => Enumerable.Range(1, 15).ToList());
}
private static List<double> MyDoubleListCreator()
{
return Enumerable.Range(1, 10).Select(Convert.ToDouble).Select(val => val + 0.3).ToList();
}
}
This can than be used in these ways:
internal class Program
{
private static void Main(string[] args)
{
ListCreator.Register(SelfMadeList);
var someIntegers = ListCreator.Create<int>();
foreach (var item in someIntegers)
{
Console.WriteLine("Some integer: " + item);
}
var someDoubles = ListCreator.Create<double>();
foreach (var item in someDoubles)
{
Console.WriteLine("Some doubles: " + item);
}
var someTimeSpans = ListCreator.Create<TimeSpan>();
foreach (var item in someTimeSpans)
{
Console.WriteLine("Some timespans: " + item);
}
Console.ReadKey();
}
private static List<TimeSpan> SelfMadeList()
{
return Enumerable.Range(1, 20)
.Select(Convert.ToDouble)
.Select(val => val + 0.5)
.Select(TimeSpan.FromHours)
.ToList();
}
}
If the types are not derived from a common class, you can return a List<Object> and then cast the individual elements when you use them.

Categories