No, there is no such option.
As you noted, for:
public T this[] { get; set; }
you need some "generic-ness" at the outer scope, as the this[]
also can't be generic on its own. So, your class/whatever would be generic-<T>
and force the users not only to specify the T, but also to specify only one single T for all elements.
For Dictionary<string, Tuple<Type, object>>
the best you can have is:
public object this[] { get; set; }
public IMyInterface this[] { get; set; }
this is because at the time of compilation, your whole dictionary-class does not have any information about the item types. The code is limited to object
as in Typle<Type,object>
. The only things you can do is to return an object
, or try to cast it to some other known type, like interface.
If you resign from using a this[]
, you can try to make a 'smart getter':
public TValue GetItem<TValue>(TKey index) { ... }
public void SetItem<TValue>(TKey index, TValue value) { ... }
which would just do all the casting (like return (TValue)tuple.Item2
). However, that would have some issues like difficult usage when used in context when "TValue" is unknown. So, you'd also probably need
public object GetItem(TKey index) { ... }
public void SetItem(TKey index, object value) { ... }
just to avoid cumbersome GetItem<object>
. Of course, wherever the TValue versions are used, the user would need to specify the TValue explicitely (except for the SetValue where it'd be probably inferred, not necessarily correctly).
Now, let's get back to basics. What is your idea about
public T this[] { get; set; }
anyways, hm? As you bundle together the Type and Object in the Tuple, it seems like you want to be able to pack a heterogenous items into the Dictionary, right? So, tell me, how the final user and/or how the code/compiler would be able to guess wnat is being returned:
var foo = myDictionary["bar"];
what would be the type of the foo
variable? Compiler can't guess, because at compilation myDictionary only know it will hold some Tuple<Type,object>
but it does not hold anything now. Also, it actually does not even exist yet, since it's being compiled.. It is simply not possible to force a 'return value' to be "typed" with the exact type taken from your Tuple
.
Also, why do you even carry the Type in that Tuple? The object
kept in the dictionary knows its Type, always. You don't need to carry the Type for it, you can simply .GetType()
on that object with just the same effect. If you'd want to preserve upcasted type information (example: have a Bar inherited from Foo, upcast Bar as Foo and put into magiccontainer, retrieve back as Foo not Bar), then you are a bit out of luck.. Even dynamic would not help with that and would return you "dynamic object" which would know itself to be "Bar" and which would allow you to dynamically call all Bar methods. I think that carrying the Type is completely unnecessary, unless you have some strong shielding/isolation requirements - but in that case, simple "carrying a Type and casting to it" will not help at all. For such things you will probably need dynamic proxies.
Since I already got chatty, last (a bit useless, but you may still want to hear it) note: It actually is possible to force a "cast" to a type stored in a 'Type'. You can do that with expressions. You can dynamically build an Expression<>
, bind it with correct Type object, and invoke.
Borrowing code from https://stackoverflow.com/a/3983342/717732 it would look like:
public static ???? castAndReturn(object item, Type type)
{
var p = Expression.Parameter(typeof(object), "i");
var c = Expression.Convert(p, type);
var ex = Expression.Lambda<Func<object, ????>>(c, p).Compile();
return ex(item);
}
but again, what return type you'd put into the ????
placeholders, hm? The only possibilities are object
, dynamic
, or a custom well-known common-base-type. Back to square one. D'oh. Sorry. No really, way.