Есть ли лучшая альтернатива, чем эта, чтобы «включить тип»? - PullRequest
299 голосов
/ 18 ноября 2008

Видя, как C # не может переключить на тип (который, как я понимаю, не был добавлен в качестве особого случая, потому что отношения - это означает, что более чем один отдельный случай может применить), есть ли лучший способ для имитации переключения типа, чем этот?

void Foo(object o)
{
    if (o is A)
    {
        ((A)o).Hop();
    }
    else if (o is B)
    {
        ((B)o).Skip();
    }
    else
    {
        throw new ArgumentException("Unexpected type: " + o.GetType());
    }
}

Ответы [ 28 ]

1 голос
/ 02 августа 2017

Вы ищете Discriminated Unions, которая является языковой особенностью F #, но вы можете добиться аналогичного эффекта, используя созданную мной библиотеку, которая называется OneOf

https://github.com/mcintyre321/OneOf

Основное преимущество перед switchif и exceptions as control flow) заключается в том, что он безопасен во время компиляции - нет обработчика по умолчанию или сбой

void Foo(OneOf<A, B> o)
{
    o.Switch(
        a => a.Hop(),
        b => b.Skip()
    );
}

Если вы добавите третий элемент в o, вы получите ошибку компилятора, так как вам нужно добавить обработчик Func внутри вызова switch.

Вы также можете сделать .Match, который возвращает значение, а не выполняет инструкцию:

double Area(OneOf<Square, Circle> o)
{
    return o.Match(
        square => square.Length * square.Length,
        circle => Math.PI * circle.Radius * circle.Radius
    );
}
0 голосов
/ 29 ноября 2018

Я использую

    public T Store<T>()
    {
        Type t = typeof(T);

        if (t == typeof(CategoryDataStore))
            return (T)DependencyService.Get<IDataStore<ItemCategory>>();
        else
            return default(T);
    }
0 голосов
/ 29 октября 2018

Да - просто используйте слегка странное название «сопоставление с образцом» из C # 7 и выше, чтобы сопоставить класс или структуру:

IObject concrete1 = new ObjectImplementation1();
IObject concrete2 = new ObjectImplementation2();

switch (concrete1)
{
    case ObjectImplementation1 c1: return "type 1";         
    case ObjectImplementation2 c2: return "type 2";         
}
0 голосов
/ 18 ноября 2008

Я согласен с Джоном по поводу наличия хэша действий для имени класса. Если вы сохраните свой шаблон, вы можете использовать вместо этого конструкцию «as»:

A a = o as A;
if (a != null) {
    a.Hop();
    return;
}
B b = o as B;
if (b != null) {
    b.Skip();
    return;
}
throw new ArgumentException("...");

Разница в том, что когда вы используете шаблон, if (foo is Bar) {((Bar) foo) .Action (); } вы делаете приведение типов дважды. Теперь, может быть, компилятор оптимизирует и выполнит эту работу только один раз - но я бы на это не рассчитывал.

0 голосов
/ 23 августа 2016

Это альтернативный ответ, который смешивает вклады ответов JaredPar и VirtLink со следующими ограничениями:

  • Конструкция переключателя ведет себя как функция и получает функции в качестве параметров для случаев.
  • Гарантирует, что он правильно построен, и всегда существует функция по умолчанию .
  • Возвращается после первого совпадения (верно для ответа JaredPar, не верно для VirtLink one).

Использование:

 var result = 
   TSwitch<string>
     .On(val)
     .Case((string x) => "is a string")
     .Case((long x) => "is a long")
     .Default(_ => "what is it?");

Код:

public class TSwitch<TResult>
{
    class CaseInfo<T>
    {
        public Type Target { get; set; }
        public Func<object, T> Func { get; set; }
    }

    private object _source;
    private List<CaseInfo<TResult>> _cases;

    public static TSwitch<TResult> On(object source)
    {
        return new TSwitch<TResult> { 
            _source = source,
            _cases = new List<CaseInfo<TResult>>()
        };
    }

    public TResult Default(Func<object, TResult> defaultFunc)
    {
        var srcType = _source.GetType();
       foreach (var entry in _cases)
            if (entry.Target.IsAssignableFrom(srcType))
                return entry.Func(_source);

        return defaultFunc(_source);
    }

    public TSwitch<TResult> Case<TSource>(Func<TSource, TResult> func)
    {
        _cases.Add(new CaseInfo<TResult>
        {
            Func = x => func((TSource)x),
            Target = typeof(TSource)
        });
        return this;
    }
}
0 голосов
/ 04 апреля 2019

Должен работать с

тип корпуса _:

как:

int i = 1;
bool b = true;
double d = 1.1;
object o = i; // whatever you want

switch (o)
{
    case int _:
        Answer.Content = "You got the int";
        break;
    case double _:
        Answer.Content = "You got the double";
        break;
    case bool _:
        Answer.Content = "You got the bool";
        break;
}
0 голосов
/ 08 апреля 2019

Если вы знаете ожидаемый класс, но у вас все еще нет объекта, вы даже можете сделать это:

private string GetAcceptButtonText<T>() where T : BaseClass, new()
{
    switch (new T())
    {
        case BaseClassReview _: return "Review";
        case BaseClassValidate _: return "Validate";
        case BaseClassAcknowledge _: return "Acknowledge";
        default: return "Accept";
    }
}
0 голосов
/ 03 января 2013

Как предполагает Пабло, подход с интерфейсом почти всегда является правильным решением. Чтобы по-настоящему использовать switch, другой альтернативой является создание собственного перечисления, обозначающего ваш тип в ваших классах.

enum ObjectType { A, B, Default }

interface IIdentifiable
{
    ObjectType Type { get; };
}
class A : IIdentifiable
{
    public ObjectType Type { get { return ObjectType.A; } }
}

class B : IIdentifiable
{
    public ObjectType Type { get { return ObjectType.B; } }
}

void Foo(IIdentifiable o)
{
    switch (o.Type)
    {
        case ObjectType.A:
        case ObjectType.B:
        //......
    }
}

Это также реализовано в BCL. Один пример - MemberInfo.MemberTypes , другой - GetTypeCode для примитивных типов, например:

void Foo(object o)
{
    switch (Type.GetTypeCode(o.GetType())) // for IConvertible, just o.GetTypeCode()
    {
        case TypeCode.Int16:
        case TypeCode.Int32:
        //etc ......
    }
}
...