Как проверить на конкретный тип интерфейса - PullRequest
0 голосов
/ 13 июня 2018

Скажем, у меня есть этот объект

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

Теперь я реализую здесь такой метод

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();
}

Пока все хорошо ( см. ).Есть ли способ сделать эту часть здесь более элегантной?в частности, если у меня есть более чем одна реализация Ibar, она становится грязной ...

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

ОБНОВЛЕНИЕ

Так что doSomething должно быть расширениемметод.Вот почему это не определено в интерфейсе.Другими словами: мне не разрешено менять интерфейс или их реализацию.

Ответы [ 3 ]

0 голосов
/ 13 июня 2018

Если я хорошо помню, вы можете поиграть с ограничениями универсального типа следующим образом:

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

Это должно заставить входящий параметр иметь нужный вам тип.Это самый элегантный способ, который я мог бы себе представить.

Другими являются typeof и другие условные операторы, которые в любом случае потребуют больше кода внутри метода (если, иначе и так далее).

РЕДАКТИРОВАТЬ: Поскольку OP, кажется, трудно понять, я написал полностью рабочий пример кода с результатом модульного тестирования Microsoft.

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

Вот вывод:

enter image description here

0 голосов
/ 13 июня 2018

Вы пытаетесь создать выделенный класс расширения?

при условии, что вы делаете это.

однако вы можете достичь своей цели с помощью 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();
  }
}

, поэтому вам потребуется звонить только на Something, используя правильный тип.

Здесь тип можетлегко получить .GetType() любого объекта (чтобы это не было проблемой)

0 голосов
/ 13 июня 2018

Я думаю, что вы можете делать проверки типов в одном месте, потому что это хороший кандидат на полиморфизм.Поскольку и bar, и bar2 реализуют Ibar, вы можете изменить определение doSomething на следующее.

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

Теперь, когда вы хотите отобразить его, именно так вы и поступаетеfoo:

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

Вы просто передаете свойство data, поскольку doSomething будет знать, что с ним делать, когда его получит.

Обновление Основываясь на вашем комментарии, я расширяю код, чтобы использовать сопоставление с образцом, как я предложил.Рассмотрим следующее

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

, где определение barX равно

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

Так что, когда вы добавляете реализацию Ibar, вы делаете это в doSomething.Весь клиентский код останется неизменным, потому что все, что ему нужно сделать, это передать Ibar методу doSomething, который использует сопоставление с шаблоном в последнем примере, чтобы определить, к какому типу относится элемент, и он действует соответственно.

Окончательное редактирование

Если вы хотите использовать перегрузки, вы можете использовать рефлексию, но я не знаю, подойдет ли вам это определение элегантности.В этом случае он не будет использовать операторы switch и будет находить ваши методы, если они относятся к типу, в котором написан этот. Рассмотрим следующее и измените определение моего ранее предоставленного 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";
}

Таким образом, вы вызываете метод ProcessThings (переименован для краткости, но он все еще может оставаться как DoSomething), и он выяснит, какую из ваших перегрузок следует вызвать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...