Reputation: 10463
I have this code (which I expect to work but it fails).. I really have no idea why. Please help
static void Main(string[] args)
{
var x = new MyKeyedCollection();
x.Add(new MyType() { Key = 400L, Value = 0.1 });
x.Add(new MyType() { Key = 200L, Value = 0.1 });
x.Add(new MyType() { Key = 100L, Value = 0.1 });
x.Add(new MyType() { Key = 300L, Value = 0.1 });
//foreach (var item in x)
for (int i = 0; i < x.Count; i++)
{
//Debug.WriteLine(item.PriceLevel);
Debug.WriteLine(x[i].Key);
}
}
}
public class MyType
{
public long Key;
public double Value;
}
public class MyKeyedCollection : KeyedCollection<long, MyType>
{
protected override long GetKeyForItem(MyType item)
{
return item.Key;
}
}
Exception:
System.Collections.Generic.KeyNotFoundException was unhandled
Message=The given key was not present in the dictionary.
Source=mscorlib StackTrace: at System.ThrowHelper.ThrowKeyNotFoundException() at System.Collections.Generic.Dictionary2.get_Item(TKey key) at System.Collections.ObjectModel.KeyedCollection
2.get_Item(TKey key) at KeyedCollectionTest.Program.Main(String[] args) in ...\Program.cs:line 25 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException:
How come it tries to get Key instead of index? Key is clearly long and not int. I am sure I used KeyedCollection before and it worked just fine for long key and int index.
I tried to compile in ver 2, 3.5, 4, 4.5 (using VS2012)...
Dont get it.
Upvotes: 3
Views: 2701
Reputation: 2234
This page does turn up in search results if trying to work out how you access a KeyedCollection item by index if you have keyed it on an int
, so it may be useful to answer that here. You do it like this:
myKeyedCollectionInstance.Items[index];
Where index
is an int
. .Items
is there because of IList
, and is protected
, so to make it available outside of the class and those classes that inherit from it, you'd have to add a public
or internal
accessor method, like this:
internal new IList<Occupation> AllItems
{
get { return this.Items; }
}
Upvotes: 0
Reputation: 1
You can also simply use:
Debug.WriteLine(x.ElementAt(i).Key);
"ElementAt" allows you to retrieve the element at the specified index. This works nicely if your keys are of type int.
Upvotes: 0
Reputation: 11
class Program
{
static void Main(string[] args)
{
var x = new MyKeyedCollection();
x.Add(new MyType() { Key = 110L, Value = 0.1 });
x.Add(new MyType() { Key = 122L, Value = 0.1 });
x.Add(new MyType() { Key = 233L, Value = 0.1 });
x.Add(new MyType() { Key = 344L, Value = 0.1 });
foreach(int key in x.Keys())
{
Console.WriteLine(x[key].Key);
}
Console.Read();
}
}
public class MyType
{
public long Key;
public double Value;
}
public class MyKeyedCollection : KeyedCollection<long, MyType>
{
protected override long GetKeyForItem(MyType item)
{
return item.Key;
}
public IEnumerable<long> Keys()
{
foreach (MyType item in this.Items)
{
yield return GetKeyForItem(item);
}
}
}
Wouldn't method for iterating over keys resolve problem here, or am I missing something?
Upvotes: 0
Reputation: 1500855
How come it tries to get Key instead of index? Key is clearly long and not int.
But int
is convertible to long
, so it's a valid candidate member.
The problem is that the this[TKey key]
indexer is originally declared in KeyedCollection
, whereas the this[int index]
indexer is originally declared in Collection
. The rules of overload resolution specify that the most derived class is searched first, and only the members which are first declared in that type are considered to start with. Only if that search fails does the compiler move up to the next level in the type hierarchy.
So if you write:
Collection<MyType> collection = x;
for (int i = 0; i < x.Count; i++)
{
Debug.WriteLine(collection[i].Key);
}
it will work - because the compile-time type of collection
is just Collection<T>
, which only has the "int index" indexer.
Here's an example which shows the same behaviour without using generics, an indexer, or abstract classes:
using System;
class Base
{
public void Foo(int x)
{
Console.WriteLine("Base.Foo(int)");
}
}
class Derived : Base
{
public void Foo(long y)
{
Console.WriteLine("Derived.Foo(long)");
}
}
class Program
{
static void Main()
{
Derived x = new Derived();
Base y = x;
x.Foo(5); // Derived.Foo(long)
y.Foo(5); // Base.Foo(int)
}
}
See my article on overloading for some more of the interesting rules involved.
Upvotes: 7