Използване на присъствието на индексатор като подпис/договор/тип на параметъра на метода

Като пример ще използвам класовете SqlDataReader и DataRow: и двата дефинират следния индексатор:

public object this[int columnIndex] { get; set; }

Кой е най-малкият тип общ знаменател, който да се използва като тип параметър на метода, така че и двата (и всеки друг клас, прилагащ същия индексатор) да могат да бъдат предавани и използвани по същия начин, напр.:

void DoSomething(??? indexedObject)
{
   string foo = indexedObject[0].ToString(); 
          // let's ignore the presence of ToString()
          // for the purpose of this question
}

object ли е? Ами ако индексираният обект не произлиза от object (мисля, че това е възможно, въпреки че е много малко вероятно).

Ако има значение, насочвам се към .NET 3.5.

Редактиране: Търся някакво изпълнение на договора, което кара повикващите да предават обекти, които прилагат споменатия индексатор.


person G. Stoynev    schedule 17.05.2013    source източник
comment
Използване на динамичен?   -  person tnw    schedule 17.05.2013
comment
Няма общ интерфейс или базов клас, който има този индексатор (освен IDataReader)   -  person SLaks    schedule 17.05.2013
comment
@tnw: динамичният беше въведен в 4.0.   -  person zimdanen    schedule 17.05.2013
comment
SLaks е прав. Ще трябва да създадете свой собствен интерфейс, който разкрива индексатора и типове адаптери, които го включват, които могат да преобразуват специфично общо извикване на индексатор в специфични за типа извиквания на индексатор и да го предадете на DoSomething (Е, това би било поне един начин )   -  person asawyer    schedule 17.05.2013
comment
Относно вашата редакция - Няма общо ограничение за тип от Този тип има индексен оператор от тип ‹xxx›   -  person asawyer    schedule 17.05.2013


Отговори (2)


Обект ли е?

Почти. Няма общ интерфейс или клас, който можете да използвате, така че object наистина е единственото гарантирано споделено нещо в йерархията.

Ами ако индексираният обект не произлиза от обект?

Това не е възможно в .NET. System.Object е основният тип на всички типове и стойностите винаги могат да се третират като обект (дори ако изискват бокс, за да работи).

Въпреки това, преминаването като object няма да осигури достъп до индексатора, освен чрез отражение.

Единственият пряк начин да направите това би бил чрез dynamic, но това изисква .NET 4 (и не е безопасен тип, което означава, че можете да получите изключения по време на изпълнение).

По-добър подход може да бъде предоставянето на Func<T, int, string>, което би ви позволило на сайта за повикване да посочите как да извлечете стойността:

void DoSomething<T>(T object, Func<T, int, string> valueExtractor)
{
    string foo = valueExtractor(object, 0); 
}

След това се обадете чрез:

DoSomething(indexedObject, (o,i) => o[i].ToString());

Това ви позволява да подадете обект и механизъм за извличане на стойност, дадена на индекс в сайта за повикване, който работи за всеки тип.


Редактиране по отношение на:

Редактиране: Търся някакво изпълнение на договора, което кара повикващите да предават обекти, които прилагат споменатия индексатор.

Няма вграден договор или интерфейс, който тези типове да изпълняват, нито някакъв начин за ограничаване на генеричен въз основа на съществуването на индексатор. Ще ви трябва различен подход, като моето предложение за използване на делегат за извличане на стойността.

person Reed Copsey    schedule 17.05.2013
comment
Във връзка с въпроса ми вероятно не мога да използвам обект, защото Syste.Object не имплементира индексатор от този тип, така че кодът дори няма да се компилира. - person G. Stoynev; 17.05.2013
comment
@G.Stoynev Затова написах Въпреки това, преминаването като обект няма да осигури достъп до индексатора, освен чрез отражение. Функционалният подход наистина е може би най-хубавият. - person Reed Copsey; 17.05.2013
comment
При алтернативния подход: добавяне на valueExtractor. Това е достатъчно обща идея, но за всички практически цели, когато стигна до такова решение, в крайна сметка си мисля, че ако принуждавам обаждащите се да предоставят този делегат, не мога ли да постигна същото чрез интерфейс, което донякъде се потвърждава от Hans Passant отговор. - person G. Stoynev; 17.05.2013
comment
@G.Stoynev Да - но тогава вие принуждавате извикващите да създадат клас, който да имплементира вашия интерфейс и да обвие истинския клас, тъй като самият клас не имплементира такъв интерфейс. Въпросът е кое е по-лошото... - person Reed Copsey; 18.05.2013

Бих казал, че решението за Func<T> делегат на Reed Copsey е най-добрият начин, но тъй като сте на c# 3.5, ще трябва да отидете и да дефинирате свои собствени делегати, Func<T> няма да е наличен. Въпреки това е много лесно.

Редактиране - Ами сега, това беше 3.5, а не 4.0.

Колкото и да си струва, ето още едно решение, което, както посочих в коментар, използва интерфейс за дефиниране на типове адаптери, за да разбере как да извика индексаторите на специализирани типове, но позволява на сайта за повикване (DoSomething) да работи с общия интерфейс:

void Main()
{
    var t1 = new Type1(); // ie Sql Reader
    var t2 = new Type2(); // ie DataRow

    DoSomething(new Type1IndexAdapter(t1));
    DoSomething(new Type2IndexAdapter(t2));
}

public void DoSomething(ICanIndex indexer)
{
    var r = indexer["test"];
}

public interface ICanIndex
{
    string this[string index]{get;}
}

public class Type1IndexAdapter : ICanIndex
{
    public Type1 value;
    public Type1IndexAdapter(Type1 val)
    {
        this.value = val;
    }
    public string this[string index]
    {
        get
        {
            return this.value[index];
        }
    }
}

public class Type2IndexAdapter : ICanIndex
{
    public Type2 value;
    public Type2IndexAdapter(Type2 val)
    {
        this.value = val;
    }
    public string this[string index]
    {
        get
        {
            return this.value[index];
        }
    }
}

public class Type1 // ie SqlDataReader 
{
    public string this[string index]
    {
        get
        {
            Console.WriteLine("Type 1 indexer called: " + index);
            return null;
        }
    }
}


public class Type2 // ie DataRow 
{
    public string this[string index]
    {
        get
        {
            Console.WriteLine("Type 2 indexer called: " + index);
            return null;
        }
    }
}
person asawyer    schedule 17.05.2013
comment
Func‹T› и свързаните с тях бяха добавени в .NET 3.5: msdn.microsoft.com/en-us/library/bb549151(v=vs.90).aspx - person Reed Copsey; 18.05.2013