У меня есть два интерфейса 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
}
Получение собственности с 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 здесь, вероятно, плохая практика. Вероятно, вам следует либо исправить ваши интерфейсы, либо создать перегрузки для вашего метода.
И еще один лайнер, использующий linq:
fieldNames.AddRange(
fields.Select(obj => (obj as IndexField)?.Name ?? (obj as BatchField)?.Name));
Смотрите живое демо на .Net fiddle.
Хотя в идеале вы должны изменить IndexField
и BatchField
для реализации общего интерфейса, как я написал в комментариях к вопросу.
Name
объекте имеет значение null, так что я неохотно обращаюсь с нулями здесь.
В вашем последнем выражении 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();
Как насчет просто с помощью
var fieldNames = fields.OfType<IndexField>().Select(i => i.Name)
.Union(fields.OfType<BatchField>().Select(b => b.Name))
.ToList();
Name
, потому чтоfld
является типом объекта. Почему бы не создать другой интерфейс и не наследовать от него оба интерфейса, а затем изменить типfld
в вашем последнемforeach
сobject
на этот вновь созданный интерфейс?