rst
rst

Reputation: 2714

How to check for specific type of an interface

Say I have this object

public class foo 
{
    Ibar data {get; set;}
}

public interface Ibar
{
    int id { get; set; }
}

public class bar : Ibar
{
    public int id {get; set;}
    public string a {get; set;}
}


public class bar2 : Ibar
{
    public int id {get; set;}
    public DateTime b {get; set;}
}

Now I implement a method like this one here

public something Method(foo f)
{
    if(f.data is bar)
        Console.WriteLine(((bar)f.data).doSomething());
    else
        Console.WriteLine(((bar2)f.data).doSomething());
}

public string doSomething(this bar b)
{
    return b.a;
}

public string doSomething(this bar2 b)
{
    return b.b.ToString();
}

So far so good (see). Is there a way to make this part here more elegant? in particular if I have more than one implementations of Ibar, it gets messy...

    if(f.data is bar)
        Console.WriteLine(((bar)f.data).doSomething());
    else
        Console.WriteLine(((bar2)f.data).doSomething());

UPDATE

So doSomething should be an extension method. That's why it isn't defined in the interface. So in other words: Im not allowed to change the interface or their implementations.

Upvotes: 0

Views: 116

Answers (3)

Liquid Core
Liquid Core

Reputation: 1

If I remember well you can play with generic type constraints like this:

public something Method<T>(T f) where T : IBar
{
    //Stuff here, where assures you f is always IBar
}

This should force the incoming parameter to be of the type you want. This is the most elegant way I could think of.

Other are typeof and other conditional statements, which would require more in-method code anyway (if, else and so on).

EDIT: Since OP seems having a hard time to understand I wrote a fully working code sample with Microsoft Unit Testing result.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests_ALL
{
    [TestClass]
    public class UnrelatedTests
    {


        [TestMethod]
        public void InterfaceTest()
        {
            bar x = new bar { result = "Hi bar it's " };
            bar2 xx = new bar2 { beforeResult = DateTime.Now };

            Console.WriteLine(Method(x));
            Console.WriteLine(Method(xx));

        }

        public string Method<T>(T f) where T : Ibar
        {
            return f.result;
        }

    }

    public class foo
    {
        public foo(Ibar fooBar)
        {
            data = fooBar;
        }
        Ibar data { get; set; }
    }
    public interface Ibar
    {
        int id { get; set; }
        string result { get; set; }
    }

    public class bar : Ibar
    {
        public int id { get; set; }
        public string result { get; set; }
    }


    public class bar2 : Ibar
    {
        public int id { get; set; }
        public DateTime beforeResult
        {
            set { result = value.ToString(); }
        }
        public string result { get; set; }
    }

    public static class Extensions
    {

        public static string doSomething(this bar b)
        {
            return b.result;
        }

        public static string doSomething(this bar2 b)
        {
            return b.result;
        }
    }
}

Here's the output:

enter image description here

Upvotes: 0

Amit
Amit

Reputation: 1857

Are you trying to make dedicated extention class?

assuming you are doing so.

however you can achieve your goal with dynamic

public static class BarExtensions
{
  public static string Something(this Ibar b)
  {
      return doSomething((dynamic)b);
  }

  public static string doSomething( bar b)
  {
     return b.a;
  }

  public static string doSomething(bar2 b)
  {
     return b.b.ToString();
  }
}

so, you will required to call Something only, with Proper Type.

Here Type can be easily get by .GetType() of any object (so it won't be a problem)

Upvotes: 2

Fabulous
Fabulous

Reputation: 2423

I think you can do the type checks in one place because this is a good candidate for polymorphism. Since both bar and bar2 implement Ibar, you can change the definition of doSomething to the following.

public string doSomething(Ibar item)
{
    return item is bar ? ((bar)item).a : ((bar2)item).b.ToString();
}

Now when you want to display it, this is how you handle your foo:

public void Method(foo f)
{
    Console.WriteLine(f.data);
}

You just pass in the data property because the doSomething will know what to do with it when it receives it.

Update Based on your comment, I'm expanding the code to use pattern matching as I suggested. Consider the following

public string doSomething(Ibar item)
{
    switch (item)
    {
        case bar b:
            return b.a;
        case bar2 b:
            return b.b.ToString();
        case barX b:
            return b.X.ToString();
        default:
            return item.GetType().ToString(); // Just in case...
    }
}

where the definition of barX is

public class barX : Ibar
{
    public int id {get; set; }
    public object X {get; set; }
}

So whenever you add an implementation of Ibar, you cater for it in doSomething. All client code will remain unchanged because all they have to do is pass an Ibar to the doSomething method which uses pattern matching in the last example to determine what type the item is and it acts accordingly.

Final Edit

Seeing as you want to use overloads, you can use reflection but I don't know if that will qualify for your definition of elegant. In this case, it will not use switch statements and will find your methods as long as they are in the type this one is written in. Consider the following and change the defintion of my previously supplied DoSomething.

public static string ProcessThings(Ibar item)
{
    var theType = item.GetType();
    Console.WriteLine(theType.Name);
    MethodInfo method = typeof(Program).GetMethods()
        .Where(x => x.IsStatic && x.Name == "DoSomething" && x.ToString().Contains(theType.Name))
        .SingleOrDefault();

    var ret = method.Invoke(null, new object[] { item });

    return ret?.ToString();
}

public static string DoSomething(barX item)
{
    return "foo";
}

public static string DoSomething(bar2 item)
{
    return "bar";
}

This way, you call the ProcessThings method (renamed for brevity but it can still stay as DoSomething) and it will figure out which one of your overloads to call.

Upvotes: 1

Related Questions