Несколько обращений в операторе switch с одним и тем же псевдонимом

#c# #entity-framework #c#-7.3

#c# #entity-framework #c #-7.3

Вопрос:

я хотел бы знать, есть ли мнение, как объединить 2 случая моего переключателя, которые почти одинаковы, но один имеет значение, равное нулю, а второй — нет.

         switch (rangeA)
        {
            case Range<int> intRangeA:
            {
                if (rangeB is Range<int> intRangeB)
                {
                    return intRangeA.ValueFrom <= intRangeB.ValueTo amp;amp; intRangeA.ValueTo >= intRangeB.ValueFrom;
                }

                return false;
            }

            case Range<int?> intRangeA:
            {
                if (rangeB is Range<int?> intRangeB)
                {
                    return intRangeA.ValueFrom <= intRangeB.ValueTo amp;amp; intRangeA.ValueTo >= intRangeB.ValueFrom;
                }

                return false;
            }
        }
 

Комментарии:

1. Ваш код компилируется и выполняется успешно ? В чем конкретно ваша проблема?

2. Мне не нравится, что у меня есть 2 «одинаковых» случая

3. О, так вы хотите объединить два случая, а не разделить их?

4. ах, да, моя вина… извиняюсь за неправильное слово

5. if Предложения также соответствуют шаблону. Вы могли бы включить их в case предложение вместо того, чтобы повторять одну и ту же проверку каждый раз, сопоставляя с (rangeA,rangeB) tupple.

Ответ №1:

Это скорее зависит от того, каков тип данных rangeA and rangeB .

Предполагая, что это так object , вы можете сделать что-то вроде этого. Он будет генерировать исключение во время выполнения, если вы создадите Range<Something non-comparable> для него вызов then ContainsInclusive . Вы можете добавить дополнительную проверку для этого, если хотите, но это становится немного запутанным, поскольку Nullable<T> не реализует никаких интерфейсов, поэтому вам придется прибегнуть к рефлексии.

 public class Program
{
    public static void Main()
    {
        Foo(new Range<int>() { ValueFrom = 1, ValueTo = 10 }, new Range<int>() { ValueFrom = 0, ValueTo = 10 });
        Foo(new Range<int?>() { ValueFrom = 1, ValueTo = 10 }, new Range<int?>() { ValueFrom = 0, ValueTo = 10 });
    }
    
    private static bool Foo(object rangeA, object rangeB)
    {
        return (rangeA, rangeB) switch
        {
            (Range<int> a, Range<int> b) => b.ContainsInclusive(a),
            (Range<int?> a, Range<int?> b) => b.ContainsInclusive(a),
            _ => false,
        };
    }
}

public class Range<T> 
{
    public T ValueFrom { get; set; }
    public T ValueTo { get; set; }
    
    public bool ContainsInclusive(Range<T> other)
    {
        return Comparer<T>.Default.Compare(other.ValueFrom, this.ValueTo) <= 0 amp;amp;
            Comparer<T>.Default.Compare(other.ValueTo, this.ValueFrom) >= 0; 
    }
}
 

Если вы не можете использовать новые выражения переключения таким образом, вы потенциально можете сделать что-то вроде:

 private static bool Foo(object rangeA, object rangeB)
{
    return TryContainsInclusive<int>(rangeA, rangeB) ||
        TryContainsInclusive<int?>(rangeA, rangeB);
}

private static bool TryContainsInclusive<T>(object a, object b)
{
    if (a is Range<T> rangeA amp;amp; b is Range<T> rangeB)
    {
        return rangeB.ContainsInclusive(rangeA);
    }
    
    return false;
}
 

Если rangeA и rangeB могут быть универсальными типами, вы можете обойтись более простым:

 private static bool Foo<T>(Range<T> rangeA, Range<T> rangeB)
{
    return rangeB.ContainsInclusive(rangeA);
}
 

Если rangeA и rangeB может быть каким-то базовым Range типом, то вы можете сделать что-то вроде этого. Опять же, это приведет к сбою во время выполнения, если T не сопоставимо:

 public class Program
{
    public static void Main()
    {
        Foo(new Range<int>() { ValueFrom = 1, ValueTo = 10 }, new Range<int>() { ValueFrom = 0, ValueTo = 10 }).Dump();
        Foo(new Range<int?>() { ValueFrom = 1, ValueTo = 10 }, new Range<int?>() { ValueFrom = 0, ValueTo = 10 }).Dump();
    }
    
    private static bool Foo(Range rangeA, Range rangeB)
    {
        return rangeB.ContainsInclusive(rangeA);
    }
}

public abstract class Range
{
    public abstract bool ContainsInclusive(Range other);
}

public class Range<T> : Range
{
    public T ValueFrom { get; set; }
    public T ValueTo { get; set; }
    
    public override bool ContainsInclusive(Range other)
    {
        if (other is Range<T> o)
        {
            return Comparer<T>.Default.Compare(o.ValueFrom, this.ValueTo) <= 0 amp;amp;
                Comparer<T>.Default.Compare(o.ValueTo, this.ValueFrom) >= 0;    
        }
        
        return false;
    }
}
 

Комментарии:

1. ну, да, ваш последний фрагмент помог.. я все еще занимаюсь проблемой, которую мы используем C # 7.3 в моей компании, но это классное решение, спасибо 🙂

2. @Qhori Смотрите мое редактирование — это еще один потенциальный способ

Ответ №2:

Вы также можете решить эту проблему, используя неабстрактный базовый класс Range . Range<int> Range<int?> Оба и затем присваиваются этому базовому классу.

 class Range
{
    public object ValueFrom { get; protected set; }
    public object ValueTo { get; protected set; }
}

class Range<T> : Range
{
    public new T ValueFrom
    {
        get {
            return (T)base.ValueFrom;
        }
        set {
            base.ValueFrom = value;
        }
    }

    public new T ValueTo
    {
        get {
            return (T)base.ValueTo;
        }
        set {
            base.ValueTo = value;
        }
    }
}
 

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

Затем мое решение состоит в проверке типа значений From и To . Это также хорошо работает для типов с нулевым значением. Если a nullable равно null, то nullable is int i уступает false , в противном случае, если у нас есть a Range<int?> , он будет присвоен nullable.Value i .

Это также позволяет сравнивать a Range<int> с a Range<int?> .

 Range rangeA = new Range<int> { ValueFrom = 5, ValueTo = 12 };
Range rangeB = new Range<int?> { ValueFrom = 10, ValueTo = 18 };

if (rangeA.ValueFrom is int aFrom amp;amp; rangeA.ValueTo is int aTo amp;amp;
    rangeB.ValueFrom is int bFrom amp;amp; rangeB.ValueTo is int bTo) {

    return aFrom <= bTo amp;amp; aTo >= bFrom;
}
return false;
 

Возможным улучшением является наличие строго типизированной резервной переменной в общем варианте:

 private T _valueFrom;
public new T ValueFrom
{
    get {
        return _valueFrom;
    }
    set {
        base.ValueFrom = _valueFrom = value;
    }
}
 

По крайней мере, возврат значения не требует распаковки при работе с общим диапазоном.

Комментарии:

1. Нет, потому что мне нужен большой коммутатор, где я хочу добавить другие типы данных как десятичные, а лучшая версия уже добавлена.. но спасибо 🙂