Ok, I know this is impossible, but I'm wondering if anyone knows of a way to get around this.
List<int> numberList = new List<int>();
List<object> objectList = (List<object>)numberList;
This generates the following error:
Cannot convert type 'System.Collections.Generic.List{int}' to 'System.Collections.Generic.List{object}'
This is a simple example, but here is my real problem. I have a method that dynamically generates an IQueryable{IGrouping{TKey, TElement}} instance. However, since in my case TKey is dynamically generated, I can only return an IQueryable to the caller.
I want to be able to return an IQueryable{IGrouping{dynamic, TElement}} to the caller. However, the compiler complains because just like in my first example, its casting TKey as an object (for dynamic), and won't let me cast it that way. Is there any way around that?
Update:
Here is a closer example of what I'm trying to solve:
List<int> testList = new List<int>();
var qry = new EnumerableQuery<int>( testList );
var objectQry = (IQueryable<object>)qry;
While this example doesn't produce a compile-time error, is does produce a casting exception when you try to run it. For some reason, even though IQuerable is covariant (thanks Servy for your comments on that), once I introduce an implementing class such as EnumerableQuery, it fails.
Try this.
List<int> numberList = new List<int>();
List<object> objectList = numberList.Cast<object>().ToList();
Enumerable.Cast<T>
Casts the elements of an IEnumerable to the specified type.
You need to construct a copy of each new dictionary, because dictionaries as well as lists are not covariant with respect to any of their generic arguments.
(The keys are also provided in output through the Keys property, and the sequence of pairs itself, so even IDictionary<TKey, TValue> cannot be covariant with respect to TKey.)
You can use Select to map each object to a new object:
List<Dictionary<int, string>> listOfDicts = new List<Dictionary<int, string>>();
List<Dictionary<object, string>> newList = listOfDicts.Select(dic =>
dic.ToDictionary(pair => (object)pair.Key, pair => pair.Value))
.ToList();
IQueryable<T> on the other hand is covariant, as is IGrouping, so you can cast interfaces of those types up:
IQueryable<IGrouping<dynamic, int>> query = null;
IQueryable<IGrouping<object, int>> otherQuery = query;
That compiles and runs just fine.
Related
GIVEN:
If you have the values:
Type type
IEnumerable enumerable
And the following conditions are met:
typeof(IEnumerable).IsAssignableFrom(type)
enumerable.All(element => element.GetType() == type.GetElementType())
GENERAL QUESTION:
Is it possible to create an instance of type via reflection that contains all of the elements of enumerable?
BACKGROUND:
Most of the types in System.Collections have a constructor like Example(ICollection), and if type has a constructor like that it is simple and straightforward to do Activator.CreateInstance(type, enumerable). For types like Dictionary<TKey, TValue> though, it is not that simple. The only solution I have thought of looks like this:
var dictionary = (IDictionary) Activator.CreateInstance(type);
var elementType = enumerable.First().GetType();
var key = elementType.GetProperty("Key");
var value = elementType.GetProperty("Value");
foreach (var element in enumerable)
{
dictionary.Add(key.GetValue(element), value.GetValue(element));
}
I would be more willing to accept this solution of KeyValuePair<TKey, TValue>implemented an interface which contained the properties Key and Value so you could say:
var keyValuePair = (IKeyValuePair) element;
dictionary.Add(keyValuePair.Key, keyValuePair.Value);
rather than relying on reflection to get the aforementioned property values.
This solution would only work for types within System.Collections or custom types that strongly adhere to the definitions of said types.
SPECIFIC QUESTION:
Is there a more elegant way of converting enumerable to the type of typethat also could account for edge cases like MyCollection : ICollection, where the type definition is not known to us?
UPDATE:
Here is an example:
var original = new Dictionary<int, string>
{
//values
};
var type = original.GetType();
var enumerable = original.AsEnumerable();
var copy = (Dictionary<int, string>) DoSomeMagic(type, enumerable);
object DoSomeMagic(Type type, IEnumerable enumerable)
{
//Add magic here
}
This is one of the few reasons left to use good old ArrayList.
System.Array ConvertUnknownIEnumerableToArray(IEnumerable ienumerable, Type type)
{
var list = new ArrayList();
foreach (var i in ienumerable) list.Add(i);
return list.ToArray(type);
}
The above creates a strongly typed array (named array) of the concrete objects contained in any enumerable named ienumerable. An array, of course, implements ICollection.
This technique allows you to avoid Reflection figuring out which generic MethodInfo to create and invoke to create the array. The framework does it for you.
So for testing purposes I need to have three IEnumerables of type <T>. One with both branches, one with one branch, and one comprising of just the node. I have to create further tests in much larger tree structures that will require more iterations of different IEnumerables. Currently, my (working) solution looks something like this. But I can't help but think that I could be adding the branches one at a time and setting the IEnumerables equal to one another at each step, or something of that nature to speed up the process. Evidently a direct implementation of this wouldn't work as they are reference types so they'll end up with the same value anyways, and it would be rather messy anyhow, if I worked around it...leaving me a bit stuck. Is there any way to accomplish this in less cumbersome fashion?
// Create test IEnumerables for comparison (one full, one with one branch, one of just node)
List<T> testlistfull, testlist1branch, testlistnode;
testlistnode = new List<T>();
testlistnode.Add(node0);
testlist1branch = new List<T>();
testlist1branch.Add(node0);
testlist1branch.Add(branch1);
testlistfull = new List<T>();
testlistfull.Add(node0);
testlistfull.Add(branch1);
testlistfull.Add(branch2);
IEnumerable<T> testenumfull = testlistfull as IEnumerable<T>;
IEnumerable<T> testenum1branch = testlist1branch as IEnumerable<T>;
IEnumerable<T> testenumnode = testlistnode as IEnumerable<T>;
You could use implicitly typed arrays. They infer their type from the values in the array initializer.
IEnumerable<T> testenumfull = new [] {node0, branch1, branch2};
IEnumerable<T> testenum1branch = new [] {node0, branch1};
IEnumerable<T> testenumnode = new [] {node0};
Or if they have to be of type List<T> for some reason.
IEnumerable<T> testenumfull = new List<T>{node0, branch1, branch2};
IEnumerable<T> testenum1branch = new List<T>{node0, branch1};
IEnumerable<T> testenumnode = new List<T>{node0};
You don't need the as IEnumerable<T> as simply assigning to a variable of that type will do the implicit cast.
I have the following property declared in my C# class, that we'll call MyMainClass:
private Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>> _oMyObjByTypeIDPeriodIDStatusID = null;
public Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>> oMyObjByTypeIDPeriodIDStatusID
{
get
{
return _oMyObjByTypeIDPeriodIDStatusID;
}
set
{
_oMyObjByTypeIDPeriodIDStatusID = value;
}
}
Sometimes, through reflection I'll run something like:
string propName = "oMyObjByTypeIDPeriodIDStatusID[2][4[6]";
object o = GetReflectedProperty(oMyMainClass, propName);
But that won't work because of the indexing, so instead I'll strip out the indexes and throw them in an int array. Then I run
int idxs = new int[] {2,4,6};
string propName = "oMyObjByTypeIDPeriodIDStatusID";
object o = GetReflectedProperty(oMyMainClass, propName);
That works, but now I have an object, whose value is of type
Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>>
And now I somehow have to traverse through each index until I finally get to the last nested Dictionary and the instance of MyOtherClass. I tried the following code:
for (int i = 0; i < idxs.Length; i++)
o = ((Dictionary<int, object>)o)[idxs[i]];
But that throws an error that it can't cast
Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>>
into
Dictionary<int, object>
which makes sense. My problem is, this code is generic, so I don't know what type it will be. In other words, I use it for different properties, so I don't know how many nested dictionaries there will be, so how can I cast it properly?
If you break apart going through the layers to two steps, one to go through the nested dictionaries and one to get the final value you can use the non-generic interface IDictonary and change your for loop to
for (int i = 0; i < idxs.Length - 1; i++)
o = ((IDictionary)o)[idxs[i]];
o = ((IDictionary)o)[idxs[idxs.Length - 1]];
and that should fix your casting errors.
You could just cast it to an IDictionary, if you don't know the generic type parameters at compile-time.
Just like Dictionary<TKey, TValue>, IDictionary has an indexer, so you should be able to iterate through the inner dictionaries using that interface.
The real issue here is that Dictionary<TKey,TValue> is strongly typed, so the system doesn't recognize it as Dictionary<int,object> even though TValue is derived from object. Try this instead (using Linq):
var recastDictionary = o
.Select(x => new { Key = x.Key, Value = (object)x})
.ToDictionary(x => x.Key, x => x.Value)
;
That should recast the values and give you a Dictionary<int,object> without any hassle or loss of data. (I'm sure there's more efficient ways to do it, but that should be acceptable enough, or at least give you an idea of what you need to do.)
Why is this a problem in C#?
public Dictionary<string, IEnumerable<object>> GetProducts() {
return new Dictionary<string, IList<object>>();
}
This is not possible, because it would creata a dictionary, having IList<object> as a value.
So if you would call:
IEnumerable<object> values = someOtherObjectCollectionPerhapseSomethingFromLinq;
Dictionary<string, IEnumerable<object>> products = GetProducts();
products.Add("MyKey", values);
Your returning dictionary will accept the IEnumerable<object> but the dictionary underneat will not accept it, since it values HAVE TO BE of type IList<object>, which in this is, it isn't.
You can't return this:
new Dictionary<string, IList<object>>();
.. but you can return this:
new Dictionary<string, IEnumerable<object>>();
.. or change your return-type:
public Dictionary<string, IList<object>> GetProducts(){ ... }
Part of the reasoning behind this:
Imagine if you could get the returned Dictionary as a Dictionary<string, IEnumerable<object>>, but with the values as List<object> instead of IEnumerable<object>(where List is a more specific type of IEnumerable).
If you then tried to add a new IEnumerable<object> to it, that would be an invalid operation, since your elements are not, in fact, of type IEnumerable<object>, but rather List<object>.
The reason that is disallowed, is that an element of type List<object> might (and does) contain methods that an IEnumerable<object> does not.
Imagine adding an element of type IEnumerable<object> to a Dictionary<string, List<object>>. That would mean that you could call a List<object>-specific method on each element until you reached the first IEnumerable<object>-element, where that method would not be defined, and therefor throw an exception.
Your original code causes a compile-time error to stop you from ever getting into problems such as this.
I myself asked a related question not too long ago, which might be helpful to understand this better.
I have number of lists of type:
public List<KeyValuePair<KeyValuePair<string, string>,
List<KeyValuePair<string, string>>>> rawComparisonObject;
I want to get the intersection of these lists according to the "key" of the KeyValuePair that constructs the List
I tried:
List2 = list1.Intersect(list2).Intersect(list3)...... etc , but as you can see it Intersects all the KeyValuePair variable, not the one I want.
I also tried
Intersect lists on KeyValuePair key?
In the following form:
public List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> getCommon(List<ResourceInformation> input)
{
List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> rawComparisonObject =
new List<List<KeyValuePair<KeyValuePair<string,string>,List<KeyValuePair<string,string>>>>>();
foreach (ResourceInformation item in input)
{
rawComparisonObject.Add(item.rawComparisonObject);
}
foreach (List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>> item in rawComparisonObject)
{
}
List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> common =
new List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>>();
for (int i = 0; i < (rawComparisonObject.Count-1); i++)
{
var keysFromB = new HashSet<KeyValuePair<string, string>>(rawComparisonObject[i].Select(x => x.Key));
var result = rawComparisonObject[i+1].Where(x => keysFromB.Remove(x.Key));
common.Add(result.ToList());
}
return common;
}
it returned very faulty values,
is there
Any easy way to do this ?
I use this data structure in linked data work, to get common objects as a result of comparing between objects
Ex: Batman vs. Inception
should return:
Type : Movie | Movie
Starring : Christian Bale | Leonardo Dicaprio
of course everything is highlighted with it's URI link, that's why I need keyValuePair one for URI and other for label....
I tried my best to explain this complex data-structure. hope it's clear enough
As I understand the code you've written, here's my (revised) translation:
public List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> getCommon(List<ResourceInformation> input)
{
var rawComparisonObject =
input.Select(item => item.rawComparisonObject).ToList();
var common = rawComparisonObject.Zip(
rawComparisonObject.Skip(1),
(prevItems, nextItems) =>
(from next in nextItems
join prev in prevItems on next.Key equals prev.Key
select next).ToList()).ToList();
return common;
}
Edit: the above translation, omits the empty foreach loop in the middle and uses the join as a filter, projecting only the 'next' elements that pass the join criteria. I tend to favor join for this kind of filtering as I know it utilizes hashing under the covers to efficiently perform the matching it does.
The problem with my prior version was that it collected the join results using a 'group join' variable which led to that extra enumerable that we didn't want. After the change, the inner ToList() is analogous to the result variable in the original code sample provided in the post. The outer ToList() is the final common variable's (re)packaging of the results. I believe this will provide results similar to that of the original code; however, I strongly encourage testing to verify that the results meet expectations.
IMHO, the right thing to do would be to refactor to simplify the generics use until we can reason about them better. In a brief interim attempt I changed GetCommon to a generic type like this (later changing it back):
public List<List<KeyValuePair<T, List<T>>>> GetCommon<T>(/*List<ResourceInformation> input*/)
From there, we could promote the rawComparisonObject list to a parameter of the method - and in the process of doing so, we would replace the current parameter of the method. The use of var typing allows us to avoid changing the type for the common local variable (so long as we're careful that the output type matches the expected return type, which was my bad on the original translation.)
There are many more design ideas and questions than I could comfortably examine here, so I am going to close without attempting to do so. I do want to offer that this was a good challenge - sometimes LINQ isn't the right choice, but even when it isn't the right choice, a change of approach can make it worth trying. Thanks!
you can do this with linq, although likely you should change your data model to be more efficient:
var keys = list1.select( kv => kv.Key).intersection(list2.select(kv => kv.Key)
var result = list1.where( key => keys.contains(key).TolLst()
If you only want to intersect the KeyValuePairs on their key you should implement a custom IEqualityComparer<T> and use Intersect() method like this:
class KeyValyePairComparer : IEqualityComparer<KeyValuePair<string, string>>
{
public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
{
return x.Key == y.Key;
}
public int GetHashCode(KeyValuePair<string, string> item)
{
return item.Key.GetHashCode();
}
}
Using the implementation above you can get the intersection with the query:
var comparer = new KeyValuePairComparer();
var intersection = list1.Intersect(list2, comparer).Intersect(list3, comparer);