During the development of one of my projects, I encountered an issue regarding generic types.
The project requires me to write a class that would act as a source of list objects. Suppose I had the following class:
public class TablesProvider
{
private readonly List[] _tables;
public TablesProvider()
{
// initialize the tables var here....
}
public List<TItem> GetTable<TItem>()
{
return (List<TItem>)_tables.Single(x => x is List<TItem>);
}
}
This class obviously doesn't work, because the List type is a generic type and therefore the generic arguments should be specified.
So I made an abstract type called MyList, that would be derived by a more specific type MyList<TItem> in order to escape this requirement, and edited the TablesProvider a little.
public class TablesProvider
{
private readonly MyList[] _tables;
public TablesProvider()
{
// initialize the tables var here....
}
public MyList<TItem> GetTable<TItem>()
{
return (MyList<TItem>)_tables.Single(x => x is MyList<TItem>);
}
}
public abstract class MyList
{
// ...
}
public class MyList<TItem> : MyList, IList<TItem>
{
private readonly List<TItem> _elements = new List<TItem>();
public TItem this[int index]
{
get { return _elements[index]; }
set { _elements[index] = value; }
}
// ...
}
This works quite well. There is only one problem left. Suppose I had 45 different collections, each defined with a different generic argument. What would be the best way of initializing all of those collections? I cannot use a for loop here, since generic parameters are specified at compile-time and not at runtime, and therefore a construction like this wouldn't be possible:
for (int i = 0; i < 45; i++)
_tables[i] = new MyList<GenericParameters[i]>();
My ultimate goal is to have the luxury to just do something like this...
var table = _tablesProvider.GetTable<SomeClass>();
var element = table[3];
var propertyValue = element.SomeProperty;
... without the need to cast the variable element in order to access its type-specific members.
It is probably worth mentioning that the amount of different list objects is fixed to 45. This will not change. In theory, I could initialize the array line by line, or have 45 properties or variables instead. Both of these options, however, sound as a rather cheap solution to me, but I will accept one of them if there is no other way.
Any of you got some ideas? Am I doing this completely wrong? Should I consider an other structure?
Thanks in advance.
Yes, it is possible to do what you are describing if you use reflection.
Supposing that your hypothetical GenericParameters array is an array of Types (since you can't have an array of type identifiers), you can define this helper function:
private MyList MakeList(Type t)
{
return (MyList)Activator.CreateInstance(typeof(MyList<>).MakeGenericType(t));
}
And that will allow you to do this:
public TablesProvider()
{
var GenericParameters = new[] { typeof(string), typeof(int), typeof(DateTime) };
_tables = new MyList[GenericParameters.Length];
for (int i = 0; i < GenericParameters.Length; i++)
{
_tables[i] = MakeList(GenericParameters[i]);
}
}
You can even use LINQ if you want:
public TablesProvider()
{
var GenericParameters = new[] { typeof(string), typeof(int), typeof(DateTime) };
_tables = GenericParameters.Select(MakeList).ToArray();
}
Previous answer:
Well, the reality is that you're going to have a list of 45 different types somewhere, which pretty much means you're going to have 45 different lines of similar code. So one could say the goal is to make those lines as concise as possible.
One way to do so would be to add a helper function:
private void AddTable<T>()
{
_tables.Add(new MyTable<T>());
}
(this assumes changing _tables to a List<MyTable>)
Then you could just do:
AddTable<Type1>();
AddTable<Type2>();
AddTable<Type3>();
AddTable<Type4>();
this implementation works
public class TablesProvider
{
private readonly List<object> _tables;
public TablesProvider()
{
_tables = new List<object>();
}
public IList<TItem> GetTable<TItem>()
{
var lst = (List<TItem>)_tables.SingleOrDefault(x => x is List<TItem>);
if (lst == null)
{
lst = new List<TItem>();
_tables.Add(lst);
}
return lst;
}
}
it creates List of TItem when necessary; next time it returns the same list for TItem. it is lazy initialization
so you can do invoke
var table = _tablesProvider.GetTable<SomeClass>();
without any code like this:
for (int i = 0; i < 45; i++)
_tables[i] = new MyList<GenericParameters[i]>();
it is not ThreadSafe
Related
.NET 4.0 introduced a non-generic IList which exposes the ability to add values to the List without needing to know the generic type. This is useful because it allows me to write a method such as the following:
void CreateListFromBytes(IntPtr bytes, Type outputType, out object outputObject)
{
Type elementType = outputType.GenericTypeArguments[0];
int numberOfElements = ReadHeaderBytes(bytes);
bytes += Marshal.SizeOf(typeof(int));
IList outputList = (IList) Activator.CreateInstance(outputType);
for (int i = 0; i < numberOfElements; i++)
{
object element = ReadDataBytes(bytes, elementType);
bytes += Marshal.SizeOf(elementType);
outputList.Add(element);
}
outputObject = outputList;
}
However, when I try to implement a method with a similar style for HashSet or ISet, there is not such non-generic interface I can find that exposes and Add() method.
I am wondering if such an interface exists that I may have missed. If not, I am wondering how I can go about adding elements to object I know for certain is Set (since I created it the Activator.CreateInstance())
I would end up with a couple of aux types for constructing a set:
interface ISetBuilder
{
void Add(object item);
object Build();
}
class SetBuilder<T, TSet> : ISetBuilder where TSet : ISet<T>, new()
{
private readonly TSet _set = new TSet();
public void Add(object item)
{
if (!(item is T typedItem))
{
throw new ArgumentException();
}
_set.Add(typedItem);
}
public object Build() => _set;
}
Those types then could be used like this:
var builderType = typeof(SetBuilder<,>).MakeGenericType(elementType, outputType);
var builder = (ISetBuilder) Activator.CreateInstance(builderType);
var element = CreateElement(...);
builder.Add(element);
var set = builder.Build();
And yes, this could be generalised to support lists as well. Just replace ISet<T> with ICollection<T>.
Another possible (but a bit less robust) solution is just to find and call the specific Add method of a set by using reflection.
I'm trying to refactor the below into a generic function; the code sample below works for me if type is a specified in the function declaration. However, it fails when I try to use T.
I get an IENumerable-like object containing generic objects back from an external application, and from this want to filter out those that are of the specific type in question.
For context, these are geometric features selected on screen by the user prior to the code running. I need to validate that the correct type of thing has been picked, and return those things in a clean list.
Initial code with defined type that works:
public static List<Point> GetSelectedPoints()
{
List<Point> tmp = new List<Point>();
Selection oSel = GetSelectionObject();
for (int i = 1; i <= oSel.Count; i++)
{
try
{
if (oSel.Item(i).Value is Point)
{
Point P = (Point)oSel.Item(i).Value;
tmp.Add(P);
}
}
catch
{
throw new Exception("An error occurred whilst retrieving the selection");
}
}
return tmp;
}
Here the attempt to use T:
static public List<T> GetThisTypeFromSelection<T>()
{
Selection osel = GetSelectionObject();
List<T> tmp= new List<T>();
for(int i = 1; i<=osel.Count ; i++)
{
if (osel.Item(i).Value is T)
{
T thing = (T)osel.Item(i).Value;
tmp.Add(tmp);
}
}
return tmp;
}
osel.Item(i).Value.GetType() returns a "System.__ComObject"... Which is not helpful.
The object model of the external application is such that everything is derived from a single base class, with many layers of subclassing, something like this:
public class Base
{}
public class Geometry2d : Base
{ }
public class Line : Geometry2d
{ }
public class Circle : Line
{ }
public class Face : Geometry2d
{ }
public class PlanarFace : Face
{ }
public class CylindricalFace : Face
{ }
public class PlanarFaceDefinedThroughX : PlanarFace
{ }
public class PlanarFaceDefinedThroughY : PlanarFace
{ }
etcetera...
So, The selection object (while also deriving from base) returns a list of base object which could be...pretty much anything.
Depending on the application for this function, I might want to get, for example, everything that's a "Face" or derivative, or maybe just the PlanarFaces, or maybe even just the PlanarFaceDefinedThroughXs.
Update based on comments (kudos to mm8 pointing in the right direction)
static public List<T> GetThisTypeFromSelection<T>()
{
Selection osel = GetSelectionObject();
List<Base> listA = new List<Base>();
for(int i = 1; i<=osel.Count ; i++)
{
CATBaseDispatch CbD = osel.Item(i).Value;
listA.Add(CbD);
}
List<T> results = listA.Select(x => x).OfType<T>().ToList();
return results;
}
This approach seems to successfully filter out the right object types - but the returned list still shows them as COM objects...
If your Selection implements IEnumerable you could use Linq's OfType to filter items of desired type.
I have something like:
public interface IThing
{
string Id { get; }
}
public class Ball : IThing
{
public string Id { get; }
}
public class Car : IThing
{
public string Id { get; }
}
For my 3-4 functions I want to treat Ball and Car the same. I use the interface so I dont have to make overload methods (one for car, one for ball).
Finally, there is on function where I have different logic if its a Ball or a Car. I get a IEnumerable<IThings> and I want to cast it to either IEnumerable<Car> or IEnumerable<Ball> depending what it consists of. If it consists of a mix, I want it to fail. It has to be all cars or all balls.
I tried something like:
var things = (inputs is IEnumerable<Ball>) ? input.Locations.Cast<Ball>() : input.Locations.Cast<Car>()
But it doesn't like that. What is the recommended approach where I can have 1 variable?
Edit:
The reason why I wanted to get it into one variable is because I am sending it to an overloaded method. So I want to do this:
var things = (inputs is IEnumerable<Ball>) ? input.Locations.Cast<Ball>() : input.Locations.Cast<Car>()
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
Rather than this:
if (inputs is IEnumerable<Ball>)
{
var things = input.Locations.Cast<Ball>();
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
}
else
{
var things = input.Locations.Cast<Car>();
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
}
The problem in your attempt it this:
inputs is IEnumerable<Ball>
Because an IEnumerable<IThing> that only contains elements of type Ball is not the same type as IEnumerable<Ball>. You really have no choice but to enumerate through your collection to determine if every item matches the type you require. You could use .Cast<...>() and handle the InvalidCastException, but that's a little bit hacky. Another way would be to use OfType<...>:
var cars = inputs.OfType<Car>();
var balls = inputs.OfType<Ball>();
And now you can deal with them as you wish, for example:
if(balls.Any() && cars.Any())
{
//You're not allowed to have balls and cars together
throw new Exception(...);
}
However, you are really breaking the open/closed principle of SOLID here, it seems like you should consider at a higher level what you are trying to achieve.
You could do a convert method, but that would still break some principles, as you still have to place an if statement.
I am not sure, you are using interfaces the right way with what you want to achieve.
If you want a car to behave different from a ball in a specific situation, then the implementation in the car shall do something different than the implementation in the ball.
Do not try to tweak an interface from outside. The implementations have to do this.
Why not create a method DoMySpecialStuff in IThing and you just iterate over your enumerable in this one special method that just calls DoMySpecialStuff on all the elements?
That's the way you can avoid your if-statement.
I just saw your edit with your overloadedMethod
So it could work like this:
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
void OverLoadedFunction(IThing thing)
{
thing.DoSpecialStuff(); // This does different things in car/ball
}
You can separate balls and cars from each other using LINQ
IEnumerable<Ball> balls = things.OfType<Ball>();
IEnumerable<Car> cars = things.OfType<Car>();
If you want it to fail and like one line solutions try something like this
IEnumerable<Ball> balls = things.OfType<Ball>().Count() == things.Count() ? things.OfType<Ball>() : null; //or whatever you want
I decided to redo something I did some time ago: the retransformation of partially a enumerated IEnumerator<> to full IEnumerable<>. This solves a problem that I feel is important: you shouldn't enumerate twice "unknown" IEnumerable<> (for "unknown" I mean IEnumerable<> that you haven't built by hand in the same method but that are of unknown origin), because there is no guarantee that it can be done, and even if it can be done, you could cause the big work needed to generate the IEnumerable<> to be done twice.
public class RemainingIEnumerator<T> : IEnumerable<T>
{
public IEnumerable<T> Enumerable { get; set; }
public int Nulls { get; set; }
public T First { get; set; }
public IEnumerator<T> Enumerator { get; set; }
public IEnumerator<T> GetEnumerator()
{
var enumerator = Enumerator;
if (enumerator == null)
{
return Enumerable.GetEnumerator();
}
return GetEnumerableRemaining().GetEnumerator();
}
private IEnumerable<T> GetEnumerableRemaining()
{
var enumerator = Enumerator;
Enumerator = null;
int nulls = Nulls;
Nulls = 0;
T first = First;
First = default(T);
for (int i = 0; i < nulls; i++)
{
yield return default(T);
}
yield return first;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static bool Is<T>(IEnumerable<T> enu, Type type, out IEnumerable<T> enu2)
{
IEnumerator<T> enumerator = null;
int nulls = 0;
try
{
enumerator = enu.GetEnumerator();
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (current == null)
{
nulls++;
continue;
}
enu2 = new RemainingIEnumerator<T>
{
Enumerable = enu,
Nulls = nulls,
First = current,
Enumerator = enumerator,
};
enumerator = null;
return current.GetType() == type;
}
// Only nulls case
enu2 = new T[nulls];
return false;
}
finally
{
if (enumerator != null)
{
enumerator.Dispose();
}
}
}
The Is<T>() function returns true if the first non-null element is of the type type. It returns a new IEnumerable<> that can be used and that, through "magic", reuses the IEnumerable<> that was passed to Is<> (in some way it restitches the optional initial nulls, the first found element and the unused remaining IEnumerator<>).
Example of use:
var enu1 = new object[] { null, new Dog(), new Cat(), new Dog() };
IEnumerable<object> enu2;
// From this line onward, you should use at least one enu2!
// It is the partially unwinded enu1 that has been rewinded through
// some magic :-)
bool isDog = Is(enu1, typeof(Dog), out enu2);
if (isDog)
{
// Note the use of enu2!
foreach (Dog dog in enu2.Cast<Dog>())
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<IThing> testCollection = new List<IThing>();
testCollection.Add(new Ball());
testCollection.Add(new Car());
try
{
if (testCollection[0] is Ball)
{
Console.WriteLine(testCollection.Cast<Ball>().Count().ToString());
}
else
{
Console.WriteLine(testCollection.Cast<Car>().Count().ToString());
}
}
catch(InvalidCastException ex)
{
Console.WriteLine("Mix isn't allowed!");
}
}
}
public interface IThing
{
string Id { get; set;}
}
public class Ball : IThing
{
public string Id { get;set; }
}
public class Car : IThing
{
public string Id { get;set; }
}
This code will throw an InvalidCastException at the call to Cast<Ball> as the Car object can't be cast to Ball. This should do what you wanted, if I'm not mistaken.
The code will only check the type of the first element, as the List shouldn't be mixed, it can be assumed that all the other objects in that List should have the same type, if not that's, in my opinion and from how the question was written, appropiate cause to throw an exception.
Remember that IEnumerable<> is covariant, that is, you can substitute IEnumerable<Derived> whenever an IEnumerable<Base> is needed.
If you have only pure containers of things, that is only things of the same kind are in a given container, then you should make that container (which will be passed as an IEnumerable at some point) of that specific type. For example a factory could produce a true list of cars, a List<car>, in code like IEnumerable<Thing> things = factory.produceList(ThingTypes.Car);. Like all types, IEnumerable<> objects retain their actual type information even when they get assigned to references of a more basic type. This type can be used to distinguish the actual type of the IEnumerable<> at run-time.
Perhaps some code is easier to understand. I create two IEnumerable<I>s with elements of two distinct types which both implement the same interface I. As I said, I can assign an IEnumerable<T> to an IEnumerable<I> as long as T implements I.
using System;
using System.Collections.Generic;
namespace ConsoleApplication34
{
interface I { };
class T1 : I { }
class T2 : I { }
class Program
{
// strongly typed arrays get assigned to base type IEnumerables.
static IEnumerable<I> i1 = new T1[] { new T1(), new T1() };
static IEnumerable<I> i2 = new T2[] { new T2(), new T2() };
static void Main(string[] args)
{
// Note: compile-time type of array elements is IEnumerable<I>!
IEnumerable<I>[] iEnumArr = new IEnumerable<I>[] { i1, i2 };
foreach (IEnumerable<I> ie in iEnumArr)
{
// ... but the run-time types of the IEnumerable objects
// are actually different.
Console.WriteLine("ienumerable is of T1: " + (ie is IEnumerable<T1>));
Console.WriteLine("ienumerable is of T2: " + (ie is IEnumerable<T2>));
}
}
}
}
The output is
ienumerable is of T1: True
ienumerable is of T2: False
ienumerable is of T1: False
ienumerable is of T2: True
Edit of the edit: I see that you are working with thingsSet which is a true IEnumerable<Thing>. True, then the type tests don't work any longer.
Edit: Your edit is a bit unclear to me, but I assume that your overloaded method has two (or n) versions, one for an IEnumerable<car> and one for an IEnumerable<ball>. In that case I would do everything which is independent on the concrete type of the Thing first, and then distinguish only for the part where it matters. For example:
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
// I may see your problem: Now with thingsSet we have true
// Enumerables of Thing, and the tests below are always false.
// Hm.
var carSet = thingsSet as IEnumerable<car>;
var ballSet = thingsSet as IEnumerable<ball>;
bool results;
if(carSet != null ) { results = callOverLoadedFunction(carSet); }
else if(ballSet != null) { results = callOverLoadedFunction(ballSet); }
else { throw /*...*/}
}
This solution has a bit of a code smell; ideally the calling code whouldn't be concerned with the concrete type of things. One possibility is to leave the "branching" for the distinct types to the Things. Or if that isn't possible, provide a single callNonOverLoadedFunction(IEnumerable<Thing>) with then branches internally, invisible to the caller. These functions are probably closer to the Things implementation and "know" which different types of Things exist; your calling code does not, and does not want to know, from a maintenance perspective.
I've got a Dictionary<Type, HashSet<GenericType>> which I use to hold my data in, and I am trying to make a function that returns one of those HashSets given the generic type T : GenericType.
Basically
Dictionary<Type, HashSet<GenericType>> data;
public HashSet<T> Get<T>() where T : GenericType
{
var tp = typeof(T);
//....check if its in the dictionary, fill if not....
return data[tp];
}
This isn't valid of course. But I am having a very hard time figuring out what I should do instead.
I feel like returning T is optimal though, since you then can do something like this:
Get<Derived>().Where(x => x.DerivedProperty == someValue)
But the only thing I thought of was to create a new HashSet<T> every time Get is called, and then using a foreach-loop cast and add every item from the already existing HashSet in the Dictionary, but this feels like such a waste?
Another idea is to skip HashSet and use another (covariant?) collection. But since these collections will hold lots of data, maybe it isn't the best idea either.
So in short I am wondering what the best approach for this problem is.
Update
So this is the structure I got. The type which contains data is a type of service in my code structure. It will via reflection be loaded and initialized at runtime. From there I later use a ServiceFactory of sorts to get that service.
public class foo : Service
{
public Dictionary<Type, HashSet<BaseClass>> data = new Dictionary<Type, HashSet<BaseClass>>();
public T Get<T>() where T : BaseClass
{
var tp = typeof(T);
if (!data.ContainsKey(tp))
{
data.Add(typeof(Derived), new HashSet<BaseClass>() { new Derived(), new Derived(), new Derived() });
}
return data[tp];//this wont compile.
}
}
public class Derived : BaseClass
{
public int ExampleVariable {get;set;}
}
public abstract class BaseClass
{
// some things in here.
public void DoCommonStuff()
{
}
}
class program
{
static void Main(string[] args)
{
var service = ServiceFactory.GetService<foo>();
var collection = service.Get<Derived>();
}
}
I would just change the type of the dictionary, and cast inside your Get method. Definitely, definitely make your dictionary private though - then you can make sure that only your code (ideally only the Get method) can access it:
// Any data[typeof(Foo)] value will be a HashSet<Foo>. Only
// the Get method should access this dictionary.
private readonly Dictionary<Type, object> data = new Dictionary<Type, object>();
public HashSet<T> Get<T>() where T : GenericType
{
var tp = typeof(T);
object value;
if (data.TryGetValue(tp, out value))
{
return (HashSet<T>) value;
}
var newSet = new HashSet<T>()
// Populate newSet here
data[tp] = newSet;
return newSet;
}
I have a bit more discussion of a closely-related issue in a blog post.
Supposing I have the following classes:
class Master {
int x;
int y;
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}
class Sub1:Master {
int z;
public int Z { get { return z; } set { z = value; } }
}
class Sub2:Master {
int w;
public int W { get { return w; } set { w = value; } }
}
class Sub3:Master {
int t;
public int T { get { return t; } set { t = value; } }
}
Then I have defined three different arrays, one for each Sub* type:
List<Sub1> array1;
List<Sub2> array2;
List<Sub3> array3;
And finally I need a way to access all instances in a unified way. The idea was to use a new array List<T>[] array4 = new[] {array1, array2, array3}; and use an int as index, so I don't have to write three times the common operations for properties X and Y.
However, I can't do it in this way because the three arrays have different type. What can I use?
They share a base type so you can create a List<Master> to hold instances of all three types.
Ultimately, all objects share the same base type object so it always possible to have a List<object>, but in this case they share a base type higher in the hierarchy so you can use Master.
To create a list for all the instances you can do something like this:
var all = new List<Master>(array1.Count + array2.Count + array3.Count);
all.AddRange(array1);
all.AddRange(array2);
all.AddRange(array3);
First you create a new list and since you already know what the expected capacity should be you use the constructor overload that accepts an int capacity. This way the list does not have to be resized when you add the other collections, which leads to more efficient code.
As a side note and I know this is probably only sample code but nonetheless you should name your variables according to what they represent, so naming something like array* should be reserved for when they really represent arrays.
List implements the non-generic interfaces ICollection, IEnumerable and IList, so you can create a new array of IList[] array4 = { array1, array2, array3 };
Or, while adding elements at your Lists you could add them (also) to another list of type Master, like this
List<Sub1> array1 = new List<Sub1>();
List<Sub2> array2 = new List<Sub2>();
List<Sub3> array3 = new List<Sub3>();
List<Master> array4 = new List<Master>();
...
public void AddSub1(Sub1 sub)
{
array1.Add(sub);
array4.Add(sub);
}
public void AddSub2(Sub2 sub)
{
array2.Add(sub);
array4.Add(sub);
}
public void AddSub3(Sub3 sub)
{
array3.Add(sub);
array4.Add(sub);
}
This way, you can iterate over all elements of array4:
foreach(Master master in array4)
{
master.DoSomething();
}
You can actually make this work by using the fact that IEnumerable<T> is declared as IEnumerable<out T>, and that Enumerable.ElementAt<T> is optimized for the runtime type of the IEnumerable<T> implementing IList<T>. That lets you do this:
var x = new IEnumerable<Master>[] {array1, array2, array3};
x[0].ElementAt(4);
While still getting constant-time access to the individual elements of the lists. It feels a little clunky, but should still work.
If it fits the work being done, a better option in my opinion would be to create a generic method, and call the generic method on each of your lists:
private void MainMethod()
{
List<Sub1> array1 = new List<Sub1>();
List<Sub2> array2 = new List<Sub2>();
List<Sub3> array3 = new List<Sub3>();
DoOperation(array1);
DoOperation(array2);
DoOperation(array3);
}
private void DoOperation<T>(List<T> list) where T: Master
{
// do work here
list[0].X = 0;
}