从未知接口获取 属性

Getting property from unknown interface

我有两个接口 IndexFieldBatchField它们不共享相同的碱基class。他们俩都有一个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 ...
}

我传入了一个batchfieldsindexfields的集合作为参数。我想将名称 属性 分配给新的字符串列表。

我知道我可以将 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
}

通过反射获得 属性:

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 ...
}

我无法测试您的代码,因此您可能需要对其进行调整。

在这里使用反射可能是不好的做法。您可能应该修复您的接口或为您的方法创建重载。

如何简单地使用

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

在您最后的 foreach 语句中,您无法访问 Name 属性,因为 fld 是对象类型。您可以创建另一个 interface 并从中继承您的两个 interfaces,然后将上一个 foreach 中的 fld 类型从 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();

还有另一个使用 linq 的衬垫:

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

See a live demo on .Net fiddle.

尽管理想情况下您应该更改 IndexFieldBatchField 以实现我在问题评论中所写的通用接口。