rbaleksandar
rbaleksandar

Reputation: 9701

C# list of interface objects - casting single elements

I have a C# problem that has been bothering me for the past couple of days. I'll try to explain it based on an abstract description of what I am doing. Hope it is easy to follow. ;)

Let's say we have an interface

interface iFoo {
   void a();
}

Further I have for example 2 classes that implement this interface and the method that is in it:

class bar1 : iFoo
{
   public void a() { Console.WriteLine("bar1"); }
   public void anotherMethodBar1() { Console.Write("I love "); }
}

class bar2 : iFoo
{
   public void a() { Console.WriteLine("bar2"); }
   public void anotherMethodBar2() { Console.Write("beer"); }
}

Each class also provides an additional unique method - anotherMethodBar1() and anotherMethodBar2(). Now in my main() I want to create a list, which contains objects that implement my interface like this:

namespace ExampleFooBar
{
    class Program
    {
        static void Main(string[] args)
        {
            List<iFoo> fooBarObjects = new List<iFoo>();
            fooBarObjects.Add(new bar1());
            fooBarObjects.Add(new bar2());

            for(int i = 0; i < fooBarObjects.Count; i++)
            {
                if(fooBarObjects[i].GetType() == typeof(bar1))
                {
                    //Cast element to bar1 and use anotherMethodBar1()
                }
                if(fooBarObjects[i].GetType() == typeof(bar2))
                {
                    //Cast element to bar2 and use anotherMethodBar2()
                }
            }
        }
    }
}

As you can see I want to call each object's own (not included in the interface) method (based on the class we have anotherMethodBar1() or anotherMethodBar2(), which are NOT part of the interface). The question is - how do I do that? I'm new to C# and so far my experience had little to do with casting but right now I need it. Is this even done by using casting or is there another way? It is not possible to simply call the method

if(fooBarObjects[i].GetType() == typeof(bar1))
{
    fooBarObjects[i].anotherMethodBar1();
}

because C# doesn't understand the exact type that lies underneath and thus the available methods/functions for this object are only the standard once plus my a()-method:

I really tried to find a solution but so far only the inverse has been asked quite often - list of objects to a list of interface conversion.

Thanks a lot and best regards!

Upvotes: 3

Views: 12166

Answers (4)

user2285800
user2285800

Reputation: 9

Simply cast it :)

(fooBarObjects[i] as bar1).anotherMethodBar1();

Picking what you had

for(int i = 0; i < fooBarObjects.Count; i++)
{
   if(fooBarObjects[i].GetType() == typeof(bar1))
      (fooBarObjects[i] as bar1).anotherMethodBar1();
}

Upvotes: -1

spender
spender

Reputation: 120480

You can use the .OfType extension method to extract items of a certain type:

var allTheBar1s = fooBarObjects.OfType<bar1>();
foreach(bar1 item in allTheBar1s)
{
    //bar1 action
}
//...etc...

Of course this requires a second iteration for the bar2 items, but unless this is a hotspot, this doesn't really matter.

Perhaps preferable would be to use polymorphism and a single interface method to apply the action. This avoids any kind of requirement to test types.

interface IFoo
{
    void DoSomething();
    ...
}
class bar1 : IFoo
{
    public void DoSomething()
    {
         this.anotherMethodBar1();
    }
    ....
}
class bar2 : IFoo
{
    public void DoSomething()
    {
         this.anotherMethodBar2();
    }
    ....
}

Now:

foreach(IFoo item in fooBarItems)
{
    item.DoSomething();
}

Upvotes: 1

Tim Schmelter
Tim Schmelter

Reputation: 460158

You could use the as operator to try-cast it to the type:

for (int i = 0; i < fooBarObjects.Count; i++)
{
    var bar1 = fooBarObjects[i] as Bar1;
    if (bar1 != null)
        bar1.anotherMethodBar1();
    else {
        var bar2 = fooBarObjects[i] as Bar2;
        if (bar2 != null)
            bar2.anotherMethodBar2();
    }
}

This is the most readable and less error-prone way to cast.

Upvotes: 1

Knaģis
Knaģis

Reputation: 21485

        for(int i = 0; i < fooBarObjects.Count; i++)
        {
            if(fooBarObjects[i] is bar1)
            {
                ((bar1)fooBarObjects[i]).anotherMethodBar1();
            }
            else if (fooBarObjects[i] is bar2)
            {
                ((bar2)fooBarObjects[i]).anotherMethodBar2();
            }
        }

The key is the keyword is which checks if the object is of type bar1 (or any type derived from bar1) and the (type)object syntax which casts the object to your specified type.

An alternative option is to use as keyword which does the cast and returns null if the cast cannot be done.

        for(int i = 0; i < fooBarObjects.Count; i++)
        {
            var b1 = fooBarObjects[i] as bar1;
            if  (b1 != null)
            {
                b1.anotherMethodBar1();
            }
            else
            {
                var b2 = fooBarObjects[i] as bar2;
                if (b2 != null)
                {
                    b2.anotherMethodBar2();
                }
            }
        }

The second option is considered to be recommended over the first because the runtime only has to do the type checking once (in the as keyword) instead of twice (is and ()).

Upvotes: 9

Related Questions