Получение свойства из неизвестного интерфейса

2

У меня есть два интерфейса IndexField и BatchField. Они не разделяют один и тот же базовый класс. У них обоих есть свойство Name. Итак, учитывая этот метод

private void Initialize(IEnumerable fields)
{
    List<string> fieldNames = new List<string>();

    foreach (object fld in fields)
    {
        string name = string.Empty;

        if (fld is IndexField indexField)
        {
            name = indexField.Name;
        }
        else if (fld is BatchField batchField)
        {
            name = batchField.Name;
        }

        fieldNames.Add(name);
    }

    // Do something ...
}

Я batchfields коллекцию batchfields indexfields batchfields или indexfields в качестве параметра. Я хочу назначить свойство name новому списку строк.

Я знаю, что могу передать в List<string> fieldNames в качестве параметра метода, но мой вопрос:

Есть ли способ избежать операторов if и вызвать свойство Name хотя я не знаю правильный тип интерфейса?

Я начал с этого кода и подумал, что он будет хорошим, но, может быть, есть что-то вроде

List<string> fieldNames = new List<string>();

foreach (object fld in fields)
{
    fieldNames.Add(fld.Name); // fld might be an IndexField or BatchField interface
}
  • 1
    Почему вы не делаете перегрузки для своего метода?
  • 0
    В вашем последнем выражении foreach вы не можете получить доступ к свойству Name , потому что fld является типом объекта. Почему бы не создать другой интерфейс и не наследовать от него оба интерфейса, а затем изменить тип fld в вашем последнем foreach с object на этот вновь созданный интерфейс?
Показать ещё 1 комментарий
Теги:
oop
interface
ienumerable

4 ответа

4
Лучший ответ

Получение собственности с Reflection:

private object GetPropertyValue(object item, string property)
{
    // No value
    object value = null;

    var pi = item.GetType().GetProperty(property);

    // If we have a valid property, get the value
    if (pi != null)
        value = pi.GetValue(item, null);

    // Done
    return value;
}

Вот как это реализовать:

private void Initialize(IEnumerable fields)
{
    List<string> fieldNames = new List<string>();

    foreach (object fld in fields)
    {
        string name = GetPropertyValue(fld, "Name").ToString();
        fieldNames.Add(name);
    }

    // Do something ...
}

Я не смог протестировать ваш код, поэтому вам может понадобиться настроить его.

Использование Reflection здесь, вероятно, плохая практика. Вероятно, вам следует либо исправить ваши интерфейсы, либо создать перегрузки для вашего метода.

4

И еще один лайнер, использующий linq:

fieldNames.AddRange(
     fields.Select(obj => (obj as IndexField)?.Name ?? (obj as BatchField)?.Name));

Смотрите живое демо на .Net fiddle.

Хотя в идеале вы должны изменить IndexField и BatchField для реализации общего интерфейса, как я написал в комментариях к вопросу.

  • 1
    Это добавит пустые записи, если в списке есть другие типы, отличные от A и B, поэтому также следует добавить .Where (s => s! = Null), или на самом деле операция имела пустые строки, если они не были A или B, так добавление "?? string.empty" должно сделать.
  • 0
    @JanneMatikainen или если свойство Name объекте имеет значение null, так что я неохотно обращаюсь с нулями здесь.
1

В вашем последнем выражении foreach вы не можете получить доступ к свойству Name, потому что fld является типом объекта. Вы можете создать другой interface и наследовать от него оба interfaces а затем изменить тип fld в вашем последнем foreach с object на этот вновь созданный интерфейс. Что-то вроде этого:

public interface IBaseInterface
{
    String Name { get; set; }
}

public interface IndexField: IBaseInterface
{        
}

public interface BatchField: IBaseInterface
{
}

А потом:

foreach (BaseInterface fld in fields)
{
    fieldNames.Add(fld.Name); 
}

Или даже проще с LINQ:

List<string> fieldNames = (from IBaseInterface fld in fields select fld.Name).ToList();
1

Как насчет просто с помощью

var fieldNames = fields.OfType<IndexField>().Select(i => i.Name)
    .Union(fields.OfType<BatchField>().Select(b => b.Name))
    .ToList();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню