Creating dynamic type collections and arrays - c#

Lets say I have an interface passed to my method:
public void AlphaToChar(iList _blah)
{
}
Out of IList I want to extract it's members Type and use its type to create other Arrays or Lists in the method. See example below.
The "List = new List();" part doesn't work because, as I assume it's a type variable, not the actual type.
Any way around this ? How can I accomplish this and create a new collection of an extracted Type?
Type[] listTypes = list.GetType().GetGenericArguments();
Type listType = null;
if (listTypes.Length>0)
{
listType = listTypes[0];
}
List<listType> = new List<listType>();
Thank you.

You can do the List<> construction using the following:
// Find the generic argument type of your old list (theList)
Type genericType = theList.GetType().GetGenericArguments()[0];
// Create a new List with the same generic type as the old one
Type newListType = typeof(List<>).MakeGenericType(genericType);
// Create a new instance of the list with the generic type
var instance = Activator.CreateInstance(newListType);
But it's only going to work if you are using generic lists. The example you gave was using a regular IList. You would have to change your method signature to use a generic IList<>:
public void AlphaToChar(IList<Something> _blah) { }
Or make it even more generic:
public void AlphaToChar<T>(IList<T> _blah) /* where T : ISomething, new() */ {}
Without doing so, you should know what your IList is going to contain and you wouldn't have to use reflection to figure out what its elements types are.

This dynamically constructs a generic List<T> for the specified element type:
IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
Note that the result variable is not statically typed to the specialized list, since you don't know the type at compile time. As such, it's not possible for it to be statically typed. You're taking advantage of the fact that List<T> also implements IList here.

System.Collections.IList list =
Activator.CreateInstance(typeof(List<>)
.MakeGenericType(listTypes[0])) as System.Collections.IList;

Related

How to pass type of a ObservableCollection<T>

I have a function similar to this one and i can't edit it:
internal object DoSomething(Type type, object obj)
I need to pass type as type of an ObservableCollection but T is unknown at design time.
And this is not enough:
Type t = typeof(ObservableCollection<>);
How can i solved it?
EDIT
When using LiteDb you can map POCO class properties with LiteDb objects.
By default, an ObservableCollection returns an Array.
I need to change this default behavior passing an ObservableCollectio and get back a BsonDocument
This code works:
BsonMapper.Global.RegisterType<ObservableCollection<Phone>>
(serialize: (ObservableCollection) => OCToDoc(Client.Phones),
deserialize: (BsonDocument) => new ObservableCollection<Phone>()
);
public BsonDocument OCToDoc<T>(ObservableCollection<T> oc)
{
BsonDocument doc = new BsonDocument();
Type t = typeof(T);
BsonDocument item = new BsonDocument();
doc.Add(t.Name, item);
foreach (PropertyInfo pi in t.GetProperties())
{
string key = pi.Name;
item.Add(key, new BsonValue());
}
return doc;
}
RegisterType from LiteDb.dll is:
public void RegisterType<T>(Func<T, BsonValue> serialize, Func<BsonValue, T> deserialize);
public void RegisterType(Type type, Func<object, BsonValue> serialize, Func<BsonValue, object> deserialize);
I need to make a generic mapping for whichever type of ObservableCollection.
This means that
ObservableCollection<Phone>
must be
ObservableCollection<T>
where T isn't known at runtime.
So, how to pass an ObservableCollection in RegisterType<...> and in OCToDoc(...)
You need to call a method on the type to bind it to a generic parameter:
typeof(ObservableCollection<>).MakeGenericType(someType);
You can make the method generic
internal T DoSomething<T>(ObservableCollection<T> coll)
I made the return type T, assuming that the method will return one element of the collection. I also removed the Type type parameter, as it is replaced by the generic type parameter.
If your type is given dynamically in type, then this approach does not work.
Note that ObservableCollection<T> implements the non-generic interfaces ICollection, IList and IEnumerable. If the type is not known at design time, the best you can do, is to use one of those.
internal object DoSomething(Type type, ICollection coll)
If the method only needs to read the collection, a good approach is to use an IEnumerable<T> with T as the common base type of all your element types, assuming it is not object. Since the interface is declared as
public interface IEnumerable<out T> : System.Collections.IEnumerable
with the out keyword, it is covariant. This means, that you can supply a collection having a T' deriving from T.
Note that generics allow you to create variants of the same type at design time, but they are by no means dynamic. Since to point of generics is to give type safety, using them in dynamic scenarios (which are not type safe), they are more of a burden in dynamic scenarios.

Create delegates to generically modify properties/fields whose type is ICollection<T>

Let's say I have a simple class like so:
public class SimpleClass
{
public List<SomeOtherClass> ListOfStuff { get; } = new List<SomeOtherClass>();
}
SimpleClass itself is unimportant, let's say I've interrogated the type and determined it's of interest for some reason, so all I have is the System.Type object. Now say that I want to access any non static properties/fields on the class that implement ICollection<T> (i.e. ListOfStuff on SimpleClass). I can access/create instances of SimpleClass and I can also dynamically create instances of whatever the collection is made of, but how do I dynamically (and as efficiently as possible) clear or add items to ListOfStuff?
Basically I want to be able to create delegates that I can call later that I can pass an instance of the interested type to, and the method will clear a specific property/field on that instance. Similarly I want another delegate to which I can also pass a instance of the collection's item to (e.g. SomeOtherClass in the above example), and it will add it to the collection on the property.
I have the System.Type of the class I'm interested in, I have the PropertyInfo/FieldInfo of the field(s) I'm interested in, and I can create instances of the class and the item used in the collection.
e.g. (this is not real code!)
Type type = typeof(SimpleClass);
...
// CreateNew is a method that somehow returns a new instance of a type
object test = CreateNew(type);
// GetCollections somehow returns properties/fields that implement ICollection<>
foreach(var collection in GetCollections(type))
{
// CreateNewCollectionItem somehow returns a new instance of the item used in the collection
object newItem = CreateNewCollectionItem(collection);
// how do I implement these two lines?
var clear = Delegate.CreateDelegate(...);
var add = Delegate.CreateDelegate(...);
...
clear(test);
add(test, newItem);
}
How can I make these delegates?
UPDATE:
Perhaps I should have said "What's the best/most efficient way to produce these delegates " instead of simply "how". I'm sure I can write some code to do what is necessary, but is there some magic I can use to improve my code? Expressions perhaps?
UPDATE 2:
I'm creating my instance of the type using Expressions and was considering using a DynamicMethod or even TypeBuilder to create my delegates, as I'm not up to speed on Expressions. Does anyone have any guidance/helper classes for these as the code to produce them is not exactly readable...?
Use typeof(ICollection<>).MakeGenericType() to get the interface to ICollection<T>, and then reflection to invoke it:
var addMethod = typeof(ICollection<>).MakeGenericType(type).GetMethod("Add");
var clearMethod = typeof(ICollection<>).MakeGenericType(type).GetMethod("Clear");
clearMethod.Invoke(collection, new object[0]);
addMethod.Invoke(collection, new object[] { test });

Convert object to generic object in C#

I have a list of generic objects:
private readonly List<object> _repositories = new List<object>();
Those object are all of type either "XmlRepository<T>" or "DbRepository<T>". I also have a generic Methods that returns me the first repository of generic type argument provided:
public IRepository<T> GetRepository<T>() where T : class {...}
I do also know what type argument should return XmlRepository<T> or DbRepository<T>:
var xmlTypeVar = typeof(XmlType);
var myXmlRepo = GetRepository<xmlTypeVar>()
but I don't know how to to convert it to the correctly typed object instance...
var myConvertedXmlRepo = myXmlRepo as XmlRepository<???>
What do I have to do here? The following is not possible:
var myConvertedXmlRepo = myXmlRepo as XmlRepository<xmlTypeVar> since I'm not allowed to provide a variable as generic type argument...
This example here is somehow simplicated, so it is not possible to me to replace the type variable (xmlTypeVar) with the dedicated Type itself.
Any help is highly appreciated! Thank you very much!
use reflection to create a generic type without knowing at compile time:
Type genericXmlRepositoryType= typeof(XmlRepository<>);
Type[] typeArgs = new[] { xmlTypeVar };
var generic = genericXmlRepositoryType.MakeGenericType(typeArgs);
As written in the comments, if you want to access a Method of XmlRepository<> after creating the dynamic instance, the best idea is to create a non-generic base class and call the method there:
XmlRepository repository = (XmlRepository)generic;
repository.UseTypeSpecificMethod();

How to specify the type of an anonymous type while using a generic type

The title is pretty confusing. I will try to explain with an example. Consider the code below:
String[] str={"Apple","Banana","Cherry","Orange"};
var anoCollection=from e in str select new
{
ch=e[0],
length=e.Length
}
dataGridView.DataSource=anoCollection.ToList(); //TypeInitializationException
I feel that I need to mention the type in above case for the ToList<T>() method. But how can I mention an anonymous type here?
It is never possible to mention an anonymous type directly, but you should not need to. Generic type inference means that you don't need to specify the <T> in .ToList<T>() - the compiler will automatically inject the invented type.
There are only a few ways to refer to an anonymous type:
via someObj.GetType(), where someObj is an instance of an anonymous type
via generics, as a T, by calling a generic method via generic type inference (as in ToList())
various other usages of reflection, pulling in the T via GetGenericTypeParameters()
This may be not what you are asking for, but if you later want to use the DataBoundItem for a row, you can do it this way:
var item = TypeExtensions.CastByPrototype(row.DataBoundItem, new { ch = 'a', length = 0});
//you can use item.ch and item.length here
Trace.WriteLine(item.ch);
with the support of this method:
public static class TypeExtensions
{
public static T CastByPrototype<T>(object obj, T prototype)
{
return (T)obj;
}
}

Extract Generic Type From Enclosing Generic

I want something like this:
class Foo<T>{...}
class Boo<T>{
Queue<T> stuff = new Queue<T>();
public void Boo(Foo<T>){...};
}
...
//Extract the generic type - string - to define the type
//of MyBoo.
var MyBoo = new Boo(new Foo<string>());
I get the error "generic type 'Boo' requires '1' type arguments. Ya, I fixed the problem by stating the template type explicitly, but I'd like to know if there was/is a way to extract that type implicitly, rather than having to state it explicitly.
This other post may be related, but I'm not sure.
You can't do it implicitly directly with the constructor of a generic type, but you could from a generic method, e.g. in a non-generic class:
public static class Boo
{
public Boo<T> Create<T>(Foo<T> foo)
{
return new Boo<T>(foo);
}
}
Then:
// myBoo will be inferred to be of type Boo<string>
var myBoo = Boo.Create(new Foo<string>());
Of course, it doesn't have to be another class called Boo - it could be something completely different, and it could be an instance method of something else:
var factory = new BooFactory();
var myBoo = factory.Create(new Foo<string>());
The important point is that it's a generic method - type arguments can be inferred for generic methods, but not for generic types.
Type inference works only with methods. So if you have generic method and it is clear how to substitute generic parameter it will be substituted by compiler. For new operator it doesn't work so consider creating factory method like Create that will produce instances of Boo. Otherwise impossible.

Categories

Resources