Вопрос об индексаторах и/или дженериках

как можно узнать, реализует ли объект индексатор? Мне нужно разделить логику для DataRow и IDataReader, но у них нет общего интерфейса.

Я пробовал также с дженериками, но не знаю, какое ограничение я должен наложить на предложение where.

public class Indexer {
    // myObject should be a DataRow or a IDataReader
    private object myObject;
    public object MyObject {
        get { return myObject; }
        set { myObject = value; }
    }
    // won't compile, myObject has no indexer
    public object this[int index] {
        get { return myObject[index]; }
        set { myObject[index] = value; }
    }
    public Indexer(object myObject) {
        this.myObject = myObject;
    }
}

public class Caller {
    void Call() {
        DataRow row = null;
        IDataReader reader = null;
        var ind1 = new Indexer(row);
        var ind2 = new Indexer(reader);
        var val1 = ind1[0];
        var val2 = ind1[0];
    }
}

person Jhonny D. Cano -Leftware-    schedule 26.03.2009    source источник


Ответы (3)


Вам нужно будет объявить интерфейс со свойством индексатора, использовать этот интерфейс в качестве ограничения, а класс аргумента типа должен будет реализовать этот интерфейс, чтобы удовлетворить ограничение.

Поскольку вы не контролируете классы, которые хотите использовать, это не сработает.

В качестве альтернативы можно сделать так, чтобы класс Indexer принимал операции получения/установки в качестве отдельных параметров:

public class Indexer {

    private Func<int, object> getter;        
    private Action<int, object> setter;

    public object this[int index] 
    {
        get { return getter(index); }
        set { setter(index, value); }
    }

    public Indexer(Func<int, object> g, Action<int, object> s) 
    {
        getter = g;
        setter = s;
    }
}

public static class IndexerExtensions
{
    public static Indexer ToIndexer(this DataRow row)
    {
        return new Indexer(n => row[n], (n, v) => row[n] = v);
    }

    public static Indexer ToIndexer(this IDataReader row)
    {
        return new Indexer(n => row[n], (n, v) => row[n] = v);
    }
}

Затем вы можете сделать это:

DataRow row = null;
IDataReader reader = null;
var ind1 = row.ToIndexer();
var ind2 = reader.ToIndexer();
var val1 = ind1[0];
var val2 = ind1[0];
person Daniel Earwicker    schedule 26.03.2009
comment
Но я не могу заставить DataRow реализовать мой интерфейс - person Jhonny D. Cano -Leftware-; 27.03.2009
comment
Это похоже на Reed, за исключением того, что вам не нужно объявлять класс для обработки каждого случая — если у вас есть одноразовый, вы можете просто написать пару лямбда-выражений для создания индексатора, аналогично тому, как будет использоваться анонимный класс. в Java, чтобы сделать одноразовый класс. - person Daniel Earwicker; 27.03.2009
comment
Я думаю, что я сделаю это, я смешаю оба ответа, TX - person Jhonny D. Cano -Leftware-; 27.03.2009
comment
Обратите внимание, что IDataReader не имеет установщика - person Jhonny D. Cano -Leftware-; 27.03.2009
comment
Если вам нужна полная безопасность типов, вам придется создать ReadOnlyIndexer для покрытия таких случаев, и это может быть все, что вам здесь нужно. Или хакерский подход состоял бы в том, чтобы передать лямбда-установщик, который ничего не делает! - person Daniel Earwicker; 27.03.2009

Вы можете сделать свой Indexer абстрактным базовым классом с двумя подклассами, один для DataRow и один для IDataReader.

Чтобы упростить использование, могут существовать 2 фабричных метода, например:

var ind1 = Indexer.CreateFromDataRow(row);
var ind2 = Indexer.CreateFromIDataReader(reader);

Они могли бы создать специальный базовый класс для этого типа с собственной логикой для обработки индексации.

Это позволяет избежать накладных расходов на постоянную проверку типов для каждого вызова get/set (за счет одного виртуального свойства вместо стандартного свойства).

person Reed Copsey    schedule 26.03.2009

get { 
    DataRow row = myObject as DataRow;
    if (row != null)
        return row[index];
    IDataReader reader = myObject as IDataReader;
    if (reader != null)
        return reader[index];
}

и используйте ту же логику для set{}

person Patrick McDonald    schedule 26.03.2009
comment
Я не хочу нести расходы на кастинг [как ключевое слово], поэтому этот ответ мне не подходит, представьте, что есть ридер с 200 записями, это будет 400 приведения, я думаю, что это должно несколько снизить производительность. - person Jhonny D. Cano -Leftware-; 27.03.2009
comment
Обратите внимание, что если вы на самом деле посещаете базу данных для получения данных, стоимость 400 приведений вряд ли будет значительной. - person Daniel Earwicker; 27.03.2009
comment
Я согласен с Эрвикером. Я просто предпочитаю свой подход, потому что он позволяет вам иметь больше безопасности типов — производительность. вероятно, вполне приемлемо использовать случаи, но мой подход позволяет вам сохранять IDataReader вместо объекта. - person Reed Copsey; 27.03.2009