2009-03-26 4 views
3

Как узнать, реализует ли объект индекс?? Мне нужно разделить логику для 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]; 
    } 
} 

ответ

5

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

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

Альтернативой сделать 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]; 
+0

Но я не могу заставить DataRow реализовать свой интерфейс –

+0

См. Обновленный ответ. –

+0

i did not downvote it, FYI, извините –

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

и использовать ту же логику для набора {}

+0

Я не хочу брать на себя расходы на Кастинг ["как" ключевое слово], так что ответ мне не подходит, представьте, что есть читатель с 200 записями, это будет 400 бросков, я думаю, что это должно несколько ранить производительность –

+0

Обратите внимание: если вы действительно посещаете базу данных для получения данных, стоимость 400 бросков вряд ли будет значительной. –

+0

Я согласен с Earwicker. Я просто предпочитаю свой подход, потому что он позволяет вам иметь больше безопасности типов - перфоманс. вероятно, вполне приемлемо с использованием случаев, но мой подход позволяет вам сохранять IDataReader вместо объекта. –

3

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

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

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

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

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