Работа с неизвестным количеством неизвестных типов - .NET

2

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

Я хочу отправить коллекцию значений в метод, и внутри этого метода я хочу проверить значение против, скажем, свойства Entity. Свойство всегда будет иметь тот же тип, что и значение.

Я также хочу проверить, имеет ли значение значение null или значение по умолчанию, очевидно, в зависимости от того, является ли тип значения ссылочным типом или тип значения.

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

public static void testGenerics<TValueType>(List<TValueType> Values) {

        //test null/default
        foreach (TValueType v in Values) {
            if (EqualityComparer<TValueType>.Default.Equals(v, default(TValueType))) {
                //value is null or default for its type
            } else {
                //comapre against another value of the same Type
                if (EqualityComparer<TValueType>.Default.Equals(v, SomeOtherValueOfTValueType)) {
                    //value equals
                } else {
                    //value doesn't equal
                }
            }
        }
    }

Мои вопросы: как бы я выполнял ту же функцию, если моя коллекция содержала значения разных типов.

Мои основные проблемы - успешное определение нулевых значений или значений по умолчанию и успешное определение, если каждое переданное значение равно другому значению того же типа.

Могу ли я достичь этого, просто передав объект типа? Я также не могу использовать EqualityComparers, поскольку я не могу использовать generics, потому что я передаю неизвестное число разных типов.

существует ли решение?

спасибо

UPDATE

ok, поиск вокруг, могу ли я использовать следующий код для проверки нулевого/стандартного успешного в моем сценарии (взятого из этого SO-ответа):

object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;

Я считаю, что это может сработать.

Теперь, как я могу успешно сравнить два значения одного и того же типа, не зная их типы успешно и надежно?

Теги:
generics
types

3 ответа

1
Лучший ответ

Существует статический метод Object.Equals(object left, object right), он полагается на реализацию Equals(object) на основе одного из предоставленных аргументов. Почему вы избегаете его использования?

Правила выполнения членов равенства почти следующие:

  • Требуется: переопределить методы Equals(object) и GetHashCode()
  • Необязательно: выполните IEquatable<T> для вашего типа (это то, на что опирается EqualityComparer.Default)
  • Необязательно: выполнить == и!= операторы

Итак, как вы видите, если вы будете полагаться на Object.Equals(object left, object right), это будет лучшее решение, основанное на сильно необходимой части шаблона реализации равенства.

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

public static void TestGenerics(IList values) {
  foreach (object v in values) {
    if (ReferenceEquals(null,v)) {
      // v is null reference
    }
    else {
      var type = v.GetType();
      if (type.IsValueType && Equals(v, Activator.CreateInstance(type))) {
        // v is default value of its value type
      }
      else {
        // v is non-null value of some reference type
      }
    }
  }
}
  • 0
    круто, спасибо Алекс. Что касается ваших комментариев, я не совсем понимаю, но я думаю, что мы на правильном пути. Не могли бы вы предоставить некоторые фрагменты кода, показывающие, как я реализовал бы то, о чем вы говорите? ура
  • 0
    Просто добавил код в этот пост.
Показать ещё 1 комментарий
1

Является ли список типов, необходимых для проверки заранее определенного списка? Если это так, вы можете использовать шаблон посетителя (и, возможно, даже если нет, поскольку у нас есть Generics). Создайте метод на своих объектах (может быть выполнен с использованием частичных классов), который принимает интерфейс. Затем ваш класс вызывает метод на этом интерфейсе. Метод интерфейса может быть общим, или вы можете создать перегрузку для каждого типа, который вы хотите проверить.

Батарея, которая должна умереть, могла бы привести пример.


Пятнадцать секунд после удара "Сохранить" машина перешла в спящий режим.

Подумав об этом, шаблон посетителя может не решить вашу конкретную проблему. Я думал, что вы пытаетесь сравнить объекты, но, похоже, вы тестируете значения (так что потенциально ints и строки).

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

Шаблон "Посетитель" позволяет обрабатывать несколько типов, не указывая, как отличать конкретный тип (вы отделяете тип от элемента с помощью этого типа). Он работает, имея два интерфейса - посетитель и акцептор:

interface IAcceptor
{
  void Accept(IVisitor visitor);
}

interface IVisitor
{
  void Visit(Type1 type1);
  void Visit(Type2 type2);
  .. etc ..
}

Вы можете дополнительно использовать общий метод:

interface IVisitor
{
  void Visit<T>(T instance);
}

Основная реализация метода accept:

  void Accept(IVisitor visitor)
  {
    visitor.Visit(this);
  }

Поскольку тип, реализующий Accept(), знает, какой он тип, используется правильная перегрузка (или общий тип). Вы можете добиться того же, что и при отражении, и в таблице поиска (или в выборе), но это намного чище. Кроме того, вам не нужно дублировать поиск среди различных реализаций - различные классы могут реализовать IVisitor для создания специфичных для типа функций.

Шаблон посетителя - это один из способов сделать "Double Dispatch". Ответ на этот вопрос - это другой способ, и вы можете превратить его в нечто, что работает для вашего конкретного случая.

В принципе, длинный ответ на вашу проблему, извините.:) Проблема интригует меня, хотя - как, как вы знаете, какое свойство на сущности вы должны протестировать?

  • 0
    ха-ха, без проблем Talljoe, спасибо за внимание. Я пытаюсь поразмыслить, но если бы вы могли привести пример позже, это было бы здорово, так как я не совсем уверен, как сделать то, что вы описали. Кроме того, я предполагаю, что список типов заранее определен для каждой сущности, хотя список не обязательно будет содержать все типы и, конечно, будет отличаться для каждой сущности.
  • 0
    круто, спасибо за объяснение talljoe, высоко ценится. Что касается вашего вопроса, вы бы знали, какие поля проверять, потому что на самом деле я бы отправлял в словаре <string, object>, где ключ - это имя свойства объекта, а значение - это значение, которое нужно проверить.
Показать ещё 2 комментария
1

Короткий ответ - "да", но более длинный ответ заключается в том, что он возможен, но с вашей стороны потребуются нетривиальные усилия и некоторые предположения, чтобы заставить его работать. Ваша проблема действительно возникает, когда у вас есть значения, которые будут считаться "равными" при сравнении в строго типизированном коде, но не имеют ссылочного равенства. Вашими самыми крупными нарушителями будут типы значений, поскольку в коробке int со значением 1 не будет иметь ссылочного равенства другому коробке int того же значения.

Учитывая, что вам нужно идти по пути использования таких вещей, как интерфейс IComparable. Если ваши типы всегда будут соответствовать друг другу, это, вероятно, достаточно. Если какое-либо из ваших значений реализует IComparable, вы можете применить его к этому интерфейсу и сравнить с другим экземпляром для определения равенства (==0). Если ни один из них не реализует его, вам, скорее всего, придется полагаться на ссылочное равенство. Для ссылочных типов это будет работать, если нет специальной логики сравнения (например, перегруженный оператор == для типа).

Просто имейте в виду, что типы должны соответствовать ТОЧНО. Другими словами, int и short не обязательно будут сравниваться так же, как и int и double.

Вы также можете пойти по пути использования отражения, чтобы динамически вызывать свойство Default для общего типа, определенного во время выполнения, с помощью поставляемой переменной Type, но я бы не хотел этого делать, если бы я не делал этого для обеспечения безопасности и (или отсутствия) соображений безопасности на этапе компиляции.

  • 0
    спасибо Адам! Да, сравниваемые типы всегда будут одинаковыми. IComparable может быть путь, хотя я не знаю об этом. Не могли бы вы предоставить короткий фрагмент кода, чтобы начать меня. Кроме того, будет ли этот подход учитывать Nullable типы?
  • 0
    Эй, Адам ... Теперь, когда я думаю об этом, я не уверен, как реализовать IComparable. Я работаю с сущностями LINQ2SQL, и они не реализуют этот интерфейс. Могут ли ценности, которые я передаю, реализовать это, как?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню