views:

336

answers:

3

I have a parent class which is essentially a glorified list. It's extended by several subclasses for various functionalities.

public class HierarchialItemList<ItemType> : IEnumerable<ItemType>
    {
        public ItemType this[String itemCode]
        {
            get
            {
                foreach (IHierarchialItem curItem in hierarchialItems)
                {
                    if (curItem.Code.Equals(itemCode, StringComparison.CurrentCultureIgnoreCase))
                    {
                        return ((ItemType)curItem);
                    }
                }
                return (default(ItemType));
            }
        }
        public ItemType this[Int32 index]
        {
            get
            {
                return (hierarchialItems[index]);
            }
        }
 }

public class DatabaseList : HierarchialItemList<Database>
{
  public DatabaseList this[CommonDatabaseType typeToFilter]
    {
        get
        {
            DatabaseList returnList = new DatabaseList();
            foreach(Database curDatabase in this)
            {
                if (curDatabase.DatabaseType.Equals(typeToFilter))
                {
                    returnList.Add(curDatabase);
                }
            }
            return (returnList);
        }
    }

    public DatabaseList this[Environments.RMSEnvironment environmentToFilter]
    {
        get
        {
            DatabaseList returnList = new DatabaseList();
            foreach(Database curDatabase in this)
            {
                if (curDatabase.ParentEnvironment.Equals(environmentToFilter))
                {
                    returnList.Add(curDatabase);
                }
            }
            return (returnList);
        }
    }


}

The problem is that C# thinks that this:

Database testDatabase = sampleDatabaseList[0];

Is an error and that the indexer should be returning a DatabaseList, not a Database. You and I both know that's false. Any workarounds or do all indexers have to have the same return type?

Edit: I just figured out that it's because of using an enumeration as an indexer which is internally an integer. Still, any way to use both an enumeration and an integer?

Edit 2: As requested, here is some compiliable test code which demonstrates the problem.

using System;
using System.Collections.Generic;

namespace CSQT
{
    class A<T>
    {
        List<T> temp;

        public A()
        {
            temp = new List<T>();
        }

        public void AddItem(T itemToAdd)
        {
            temp.Add(itemToAdd);
        }

    public T this[String code]
    {
        get { return (temp[0]); }

    }

    public T this[Int32 index]
    {
        get { return (temp[index]); }

    }
}

class B : A<String>
{
    public B()
        : base()
    {
    }

    public B this[BTypes testType]
    {
        get
        {
            return (this);
        }
    }
}

enum BTypes { TEMP1, TEMP2 };

class C
{
    public C()
    {
        B temp = new B();

        //Compile error: Cannot implicitly convert type 'CSQT.B' to 'string'
        String temp2 = temp[0];

        //Compile error: Cannot convert type 'CSQT.B' to 'string'
        String temp3 = (String)temp[0];

        //This compiles and runs but instead of going to A.this[int32], it goes to B.this[BTypes testType]
        B temp4 = temp[0];
    }
}
}
+3  A: 

Why do we know that to be false? The line

Database testDatabase = sampleDatabaseList[0];

invokes the indexer with the parameter 0 which is a int literal and therefore, seeing that DatabaseList inherits from HierarchialItemList<Database> will invoke the indexer defined by

public ItemType this[Int32 itemCode] { get; }

which is declared to return an ItemType. You haven't told us what ItemType is. We have no reason to know that an ItemType can be assigned to a variable of type Database.

Indexers do not have to return the same type. However, it is not possible to overload solely on the basis of return type. That is, this is legal

class IndexerTest {
    public int this[int index] {
        get {
            return 0;
        }
    }

    public string this[double index] {
        get {
            return "Hello, success!";
        }
    }
}

This is not

class IndexerTest {
    public int this[int index] {
        get {
            return 0;
        }
    }

    public string this[int index] {
        get {
            return "Hello, fail!";
        }
    }
}

Responding to your edit:

Edit: I just figured out that it's because of using an enumeration as an indexer which is internally an integer. Still, any way to use both an enumeration and an integer?

If you want to invoke the indexer that accepts an enum, invoke it like so:

sampleDatabaseList[Environments.RMSEnvironment.SomeEnumValue];
Jason
ItemType is declared as Database when DatabaseList inherits HierarchialItemList. In this case I am not trying to invoke the enum indexer, but the integer indexer. Visual Studio and the compiler errors that a DatabaseList is being returned.
Dan
A: 

After adding the necessary classes and attributes to get your code sample to compile, I was able to run this statement with no issues:

Database testDatabase = sampleDatabaseList[0];

If you're getting an error that sampleDatabaseList[0] returns a DatabaseList, please provide a compilable code sample that contains the statement DatabaseList testDatabase = sampleDatabaseList[0];

Jake
I will do so in the morning when I'm back at work.
Dan
+1  A: 

This is perfectly valid code.

class SomeClass { }
public class A<T> : IEnumerable<T>
{

    public T this[int index]
    {
        get
        {
            return (this[index]);
        }
    }

    public T this[String index]
    {
        get
        {
            return (this[index]);
        }
    }

}
public class B : A<SomeClass>
{
    public B this[char typeToFilter]
    {
        get
        {
            return new B();
        }
    }
}


        B classList = new B();
        SomeClass test = classList[0];
Stan R.
The difference between your code and mine is that B defines an indexer by a char, not an enum. Using an enum breaks your code.
Dan