Our program has a dictionary of type:
Dictionary<TKey, TCache>();
where TKey and TCache are whatever generic types the encompassing method is given, and TCache is a CacheBase.
In another method I need to reference this object (which is stored in another dictionary where it's stored as an object) and remove a key/value pair from it. At runtime I have the TKey object boxed as an 'object' class.
How would I go about this? I can't cast to Dictionary because this method is not generic and won't know at compile time what type of cache/key it is. These are passed as parameters.
Is there a way to use reflection to cast the dictionary from Dictionary to effectively
Dictionary<CacheBase<key.GetType()>, key.GetType()>
I know this can easily fixed by not using the methods generics and instead making the Dictionary a Dictionary and making CaseBase extend an empty CacheBase, but my supervisor insisted I do it this way
Managed to fix it on my own, but for future people,
Essentially, I had to flip the perspective. I can't cast an existing dictionary to another equivalent type via reflection, however the goal was to do so so I could modify the dictionary with Types at runtime.
So instead, I made a generic method that would communicate with the dictionary in the way it wanted (if it expects a TKey, I made my method take a TKey so it would work properly).
e.g.
public void RemoveFromCache<TCache, TKey>( Type type, TKeykey ) where TCache : CacheBase<TKey> {
((Dictionary<TKey, TCache>)m_caches[type]).Remove( key );
}
After that, I simply used reflection to call my generic method at runtime with the types passed in as the appropriate generics
e.g.
var keyType = key.GetType();
var removeFromCache = GetType().GetMethod( nameof( RemoveFromCache ) ).MakeGenericMethod( type, keyType );
removeFromCache.Invoke( this, new object[] { type, key } );
And that let me communicate with a Dictionary that was boxed into an object at runtime with only types available.
Related
I'm working on a binary search algorithm, which has the following parameters:
Now when I pass these arguments:
It says that type int cannot be used as a parameter F (I was under the impression that the generic types are not concerned with types that are being passed) and that there are no 'boxing' conversion from int to IComparable.
What I was trying to do: Basically, I wanted this method to accept search key's which can be of various numeric types (ints, doubles etc.) and so in the generic method I tried to declare two types.
About this code:
The func delegate represents an object's property i.e. car.Name (string), car.Wheels(int) which are of different types. I Sort of want the key data type somehow be inferred based on the propertyFields type that's being passed, but that seems way too complicated, so I tried making it so that the F Key accepts various types and just make sure that I'm passing the correct types to it.
I don't know if this all sounds confusing, but if you have questions about any of my code, feel free to ask.
Edit: The error occurs when I call the BinarySearch Method.
Edit 2: for the propertyField i pass this as an argument: c => c.Longitude (or any other object property).
Either your 2nd constraint needs to be where F : IComparable<F> or you should not have a F at all and you should be taking in a T Key instead of a F Key
I have a class with a bunch of methods in it, the methods transfer variables elsewhere in my program when called. I want to use a dictionary as the middle man between the methods that transfer data and the methods that call them.
So here is my question. Say I make a dictionary, where the key is an int and I want the value to be the name of a method. I will assign a new value / method each time I add to the dictionary. Is there a value type I can put there that will let me do this?
Dictionary<int, ?> methodKey= new Dictionary<int, ?>();
I tried to find a list of types that dictionary will take but I couldn't find anything specific.
Thanks in advance
Use any delegate type as a type of value. For example:
Dictionary<int, Action>
So, you'll be able to write such things:
dictionary[0] = () => Console.WriteLine("0");
dictionary[1] = Foo;
dictionary[2] = a.Bar;
Specific delegate type depends on your needs - may be, you want for methods to have some input parameters or output/return values, but it should be most common type.
Will all the methods have the same signature? If so you can probably use one of the existing Action or Func delegate, (or you can create a delegate type with that signature), and use that as your second type parameter.
If not, you can use Delegate (or even object) and cast to the appropriate type when you invoke the delegates.
I have two types: let's call them A and B. A can be converted to B using an adapter method.
I then have a collection of A's in a List<A> (it could be any collection type that supports IEnumerable<A>).
I now want to convert from IEnumerable<A> to IEnumerable<B>. I know the Type of each of A and B, and I have a method to convert an A into a B, but my method and/or class is not templated itself, so I do not have access to the template type; e.g. the T in IEnumerable<T>.
I effectively want to write this ConvertCollection method, where I know "from" is of type IEnumerable<{something}>:
object ConvertCollection(object from, Type fromType, Type toType, Converter converter);
My converter looks like this:
delegate object Converter(object from);
My attempt leaves me here:
object ConvertCollection(object from, Type fromType, Type toType, Converter converter)
{
return ((IEnumerable<object>)from).Select(converter);
}
which partly works. If I call it like this
ConvertCollection(new List<A>() { new A() }, typeof(A), typeof(B), AToBConverter);
the returned collection does contain a collection of Bs, but the collection itself is of type IEnumerable<object>, not IEnumerable<B>, because I don't know how to cast to IEnumerable<{toType}>. (It matters because the result needs to be serialized).
I can attack it from the other end and create the correct return type like this:
var result = Activator.CreateInstance(typeof(List<>).MakeGenericType(toType));
// TODO: populate result here
return result;
but then the problem is that to achieve the TODO part, I need to call List<> methods on result, but I can't cast it to any type of List<> because of Co/ContraVariance rules, so even though I know the type supports List<> methods, I can't get at them to use them to populate the list; e.g. to use Add().
Is there a way to do this without using 'dynamic' and without too much reflection? I know I could locate and invoke the Add() method via reflection, but it seems like it shouldn't be necessary.
.NET 4.0 BTW
-- Clarification
As Euphoric correctly speculates, and I tried but rather badly failed to convey above, I know the types A and B at runtime, but I do not know them at compile time. Hence the direct use of generics is not an option. I do know that the collections (both supplied and as must be returned) implement the generic IEnumerable<>. That is all fixed and outside my control. (I've adjusted the title accordingly).
** Edit 2: fixed some formatting causing <> to not display (easy to accidentally omit the back-ticks!)
Using the LINQ Select method:
var result = listA.Select(a => Converter(a));
Since you are using .NET 4.0, you really should avoid using object and use generics.
The solution I settled on was to use reflection to invoke the Enumerable.Cast<> method to cast the resultant collection from IEnumerable<object> to the correct IEnumerable<> type. I got the idea from the answer to this question: Convert IEnumerable to IEnumerable<T> when T isn't known until runtime. Seems to involve very little performance penalty.
So the full answer becomes:
object ConvertCollection(object from, Type fromType, Type toType, Converter converter)
{
var partialResult = ((IEnumerable<object>)from).Select(converter);
var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(toType);
return castMethod.Invoke(null, new[] { partialResult });
}
Maybe something like this?
IEnumerable<TTo> ConvertCollection<TFrom,TTo>(object from, Converter converter)
{
return ((IEnumerable<TFrom>)from).Select(a=>(TTo)converter(a)).ToList();
}
Then you simply call it:
ConvertCollection<A,B>(new List<A>() { new A() }, AToBConverter);
I wanted to add a KeyValuePair<T,U> to a Dictionary<T, U> and I couldn't. I have to pass the key and the value separately, which must mean the Add method has to create a new KeyValuePair object to insert, which can't be very efficient. I can't believe there isn't an Add(KeyValuePair<T, U>) overload on the Add method. Can anyone suggest a possible reason for this apparent oversight?
You can use the IDictionary<TKey,TValue> interface which provides the Add(KeyValuePair<TKey,TValue>) method:
IDictionary<int, string> dictionary = new Dictionary<int, string>();
dictionary.Add(new KeyValuePair<int,string>(0,"0"));
dictionary.Add(new KeyValuePair<int,string>(1,"1"));
Backup a minute...before going down the road of the oversight, you should establish whether creating a new KeyValuePair is really so inefficient.
First off, the Dictionary class is not internally implemented as a set of key/value pairs, but as a bunch of arrays. That aside, let's assume it was just a set of KeyValuePairs and look at efficiency.
The first thing to notice is that KeyValuePair is a structure. The real implication of that is that it has to be copied from the stack to the heap in order to be passed as a method parameter. When the KeyValuePair is added to the dictionary, it would have to be copied a second time to ensure value type semantics.
In order to pass the Key and Value as parameters, each parameter may be either a value type or a reference type. If they are value types, the performance will be very similar to the KeyValuePair route. If they are reference types, this can actually be a faster implementation since only the address needs to be passed around and very little copying has to be done. In both the best case and worst case, this option is marginally better than the KeyValuePair option due to the increased overhead of the KeyValuePair struct itself.
There is such a method – ICollection<KeyValuePair<K, T>>.Add but as it is explicitly implemented you need to cast your dictionary object to that interface to access it.
((ICollection<KeyValuePair<KeyType, ValueType>>)myDict).Add(myPair);
See
List of Explicit Interface Implementations on Dictionary<K, T>'s documentation page (you'll need to scroll down).
Explicit member implementation
The page on this method includes an example.
Should somebody really want to do this, here is an Extension
public static void Add<T, U>(this IDictionary<T, U> dic, KeyValuePair<T, U> KVP)
{
dic.Add(KVP.Key, KVP.Value);
}
but i would recommend to not do this if there is no real need to do this
Unless I'm mistaken, .NET 4.5 and 4.6 adds the ability to add a KeyValuePair to a Dictionary. (If I'm wrong, just notify me and I'll delete this answer.)
https://msdn.microsoft.com/en-us/library/cc673027%28v=vs.110%29.aspx
From the above link, the relevant piece of information is this code example:
public static void Main()
{
// Create a new dictionary of strings, with string keys, and
// access it through the generic ICollection interface. The
// generic ICollection interface views the dictionary as a
// collection of KeyValuePair objects with the same type
// arguments as the dictionary.
//
ICollection<KeyValuePair<String, String>> openWith =
new Dictionary<String, String>();
// Add some elements to the dictionary. When elements are
// added through the ICollection<T> interface, the keys
// and values must be wrapped in KeyValuePair objects.
//
openWith.Add(new KeyValuePair<String,String>("txt", "notepad.exe"));
openWith.Add(new KeyValuePair<String,String>("bmp", "paint.exe"));
openWith.Add(new KeyValuePair<String,String>("dib", "paint.exe"));
openWith.Add(new KeyValuePair<String,String>("rtf", "wordpad.exe"));
...
}
As can be seen, a new object of type Dictionary is created and called openWith. Then a new KVP object is created and added to openWith using the .Add method.
just because the enumerator for the Dictionary class returns a KeyValuePair, does not mean that is how it is implemented internally.
use IDictionary if you really need to pass KVP's because you've already got them in that format. otherwise use assignment or just use the Add method.
What would be wrong with just adding it into your project as an extension?
namespace System.Collection.Generic
{
public static class DictionaryExtensions
{
public static void AddKeyValuePair<K,V>(this IDictionary<K, V> me, KeyValuePair<K, V> other)
{
me.Add(other.Key, other.Value);
}
}
}
I'm not 100% sure, but I think the internal implementation of a Dictionary is a Hash-table, which means key's are converted to hashes to perform quick look ups.
Have a read here if you want to know more about hashtables
http://en.wikipedia.org/wiki/Hash_table
First let me say, that what I want to do is get the value of a property in a generic class that may be overriden by class that inherits from it. Think of it in the base class as a default value, that the inheritor of the class can override to set their own Default value.
I have tried to use reflection directly on the type, using the System.Reflection.FieldInfo.GetValue but this does not work for classes with generic types. So I think that I need to instantiate the class to be able to see what the value is.
The "types" I have I retrieved by reading the Dlls in the bin and using Reflection to find the types that inherit from my interface.
I am using .NET 4.5
here is documentation that seems like it explains exactly what I need to do
http://msdn.microsoft.com/en-us/library/b8ytshk6.aspx
In this documentation the only difference I can see is how we got our types, I looked in the bin for types and they simply called typeof(), Since types are so complex it seems this may be a likely mis-match but I cannot see what is missing(if anything)
foreach (var item in types)
{
var ts = item.GetField("DefaultTimeToExpire");
Type[] typeArguments = item.GetGenericArguments();
if (ts != null)
{
var t = item.MakeGenericType(typeArguments);
var obj = Activator.CreateInstance(t);
var timespan = obj.DefaultTimeToExpire;
subscriberInfos.Add(new Tuple<string, Type, TimeSpan>(item.Name, item, timespan));
}
}
I am calling GetField to look for Items that have a field "DefaultTimeToExpire" so far this part works well to find the type I need.
Next I call GetGenericArguments which returns an expected array of the type Arguments.
then I call MakeGenericType
and finally Create instance wich gives me the error message
"Cannot create an instance of BusinessLogic.TestSubscriberXXX`1[Message] because Type.ContainsGenericParameters is true."
This looks like exactly what I am supposed to do.
Thanks
In order to instantiate a generic type, you need to know the actual values (types) that should be substituted for its type parameters. The GetGenericArguments() method, being a form of reflection, only gives you the type arguments, not their actual values. The values are up to you... that is the entire point of generics.
If item is a type like List<T> then item.GetGenericArguments() will return an array containing a fake "type" representing the type parameter T (with its IsGenericParameter property set to true). Therefore, passing that parameter type back into item.MakeGenericType() will simply create another open generic type equivalent to the original. To close the generic type so that it can be instantiated you need to provide an actual (non-parameter) type argument, such as int.
For example, typeof(List<>).MakeGenericType(typeof(int)) will return typeof(List<int>), while typeof(List<>).MakeGenericType(typeof(List<>).GetGenericArguments()) will simply return typeof(List<>) again. This is what is happening in your code.
I'm sorry if that is a bit opaque, I don't know how else to explain it. The bottom line is that a type like List<T> is only useful if you have a type you want to substitute in place of T.