user9945420
user9945420

Reputation:

Getting property from unknown interface

I have two interfaces IndexField and BatchField. They do not share the same base class. Both of them have a Name Property. So given this method

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

I pass in a collection of batchfields or indexfields as a parameter. I want to assign the name property to a new list of strings.

I know I can pass in List<string> fieldNames as a method parameter but my question is:

Is there a way that I can avoid the if statements and call the Name property although I don't know the correct interface type?

I started with this code and thought it would be a good one but maybe there is something like

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

foreach (object fld in fields)
{
    fieldNames.Add(fld.Name); // fld might be an IndexField or BatchField interface
}

Upvotes: 2

Views: 245

Answers (4)

fstam
fstam

Reputation: 699

Getting a property with 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;
}

Here's how to implement it:

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

I was unable to test your code so you might need to tweak it.

Using Reflection here is probably bad practice. You should probably either fix your interfaces or create overloads for your method.

Upvotes: 3

Salah Akbari
Salah Akbari

Reputation: 39946

In your last foreach statement, you can't access the Name property, because the fld is an object type. You can create another interface and inherit your both interfaces from it and then change the fld type in your last foreach from object to that newly created interface. Something like this:

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

public interface IndexField: IBaseInterface
{        
}

public interface BatchField: IBaseInterface
{
}

And then:

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

Or even simpler with LINQ:

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

Upvotes: 1

Zohar Peled
Zohar Peled

Reputation: 82474

And another one liner using linq:

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

See a live demo on .Net fiddle.

Though Ideally you should change IndexField and BatchField to implement a common interface as I wrote in the comments to the question.

Upvotes: 3

Janne Matikainen
Janne Matikainen

Reputation: 5121

How about just simply using

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

Upvotes: 1

Related Questions