записи c # 9.0 — отражение и общие ограничения

#c# #generics #c#-9.0 #record-classes

#c# #c #-9.0 #классы записей

Вопрос:

Два вопроса, касающиеся новой функции записей :

  1. Как мне распознать запись с помощью отражения? глядя [здесь] [1] может быть, есть способ обнаружить EqualityContract , но я не уверен, что это правильный путь?

  2. Возможно ли иметь общее ограничение, что общий тип является записью? то есть, возможно ли указать, что параметр типа T должен быть классом записи с использованием ограничения?

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

1. Любой псевдокод, помогающий понять проблему?

2. Почему вы спрашиваете об этом? Прежде всего, запись все еще является классом. Что касается # 2, что вы имеете в виду? Можно ли указать, что T является записью? Это всего лишь класс, поэтому, вероятно, нет

3. ©kofifus В настоящее время ведется активное обсуждение этой темы на charplang’s github под записью чемпиона Из этого комментария вы можете прочитать, что существует намерение не указывать, что класс является типом записи. В C # 10 «не будет значимой семантической разницы между записью и классом»

4. К вашему сведению, недавно была открыта проблема # 4121 , чтобы определить, является ли тип записью

5. Я думаю, что это отличный вопрос, даже если ответ «Вы не можете». Мне просто было интересно кое-что связанное: когда-нибудь не следует использовать запись вместо класса? Ответы здесь, похоже, применимы и к этому вопросу: вы не можете определить разницу, поэтому единственный раз, когда вы должны придерживаться класса, я полагаю, это если одна из встроенных функций записи (например, равенство членов) противоречит вашим намерениям.

Ответ №1:

  1. Как мне распознать запись с помощью отражения?

Если вы попробуете классы записей в sharplab.io , вы увидите, что классы записей — это обычные классы, которые реализуют IEquatable<T> интерфейс и содержат дополнительные элементы, которые используются для сравнения и клонирования экземпляров класса записей. Нет специальных атрибутов, которые указывают, что класс является record class .

Итак, я предполагаю, что нет способа определить, является ли класс классом записи с использованием отражения.

глядя сюда, может быть, есть способ обнаружить EqualityContract , но я не уверен, что это правильный путь?

С помощью отражения можно определить, обладает ли класс таким свойством, но это не является 100% гарантией того, что класс с таким свойством является классом записей.


  1. Возможно ли иметь общее ограничение, что общий тип является записью? то есть, возможно ли указать, что параметр типа T должен быть классом записи с использованием ограничения?

Это невозможно.

  1. Страница предложения записей не содержит никакой информации об указании того, что параметр общего типа T должен быть классом записи.
  2. Если вы прочитаете обсуждение под этим комментарием на Champion records странице, вы узнаете, что нет способа указать что-то вроде where T : record в C# 9 . Кроме того, планируется устранить любое значимое семантическое различие между записью и классом в C# 10 . Так что такие функции записей, как with , будут доступны и для классов. Добавление record ограничения сделает эту цель недостижимой.

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

1. Спасибо! 1- «Итак, я считаю, что нет способа определить, является ли класс классом записи с использованием отражения» — вы знаете, где было бы правильным местом для запроса определенного ответа? 2- да, я имел в виду «если возможно указать, что параметр типа T должен быть классом записи», поэтому я думаю, что нет

2. возможно, отражение может обнаружить EqualityContract ?

3. Возможно, csharplang у команды разработчиков есть дополнительная информация (или идеи) о добавлении поддержки функций из вопросов № 1 и № 2. Поэтому я думаю, что лучше задать этот вопрос непосредственно csharplang команде в репозитории C # на github .

4. perhaps reflection can detect the EqualityContract ? Можно определить, обладает ли класс таким свойством, но я не думаю, что это 100% гарантия того, что класс с таким свойством является классом записи.

Ответ №2:

Как упоминалось всеми остальными, невозможно написать

 private void MyFunc<T>(T t) where T : record {...}
  

Однако вы можете создать запись, от которой затем наследуются все типы записей, которые вы создаете. Это в значительной степени достигнет того, о чем вы просите, хотя я не знаю, как я к этому отношусь…

 public abstract record RecordMarker;
public record MyRecord : RecordMarker;
public void MyFunc<T>(T t) where T : RecordMarker
{
}
  

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

 MyFunc(new MyRecord()); // Works
MyFunc(new MyClass());  // Compiler Error
  

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

1. Это на самом деле превосходно. Я хочу указать на следующие работы: общедоступное абстрактное сообщение записи; общедоступная запись TestMessage (строка Msg, double Val) : Message; var m = TestMessage («msg», 5d); Все еще имея это однострочное определение записи, я действительно продал этот метод мне. Кроме ограничений типа, которые не указывают запись вместо сообщения (или чего-то еще) и, следовательно, являются немного менее читаемыми, недостатков нет.

2. Не работает в VisualStudio 17.5.2, ориентированном на net 6. Пробовал с базовой записью, являющейся абстрактной записью и record. Оба выдают одно и то же сообщение об ошибке: «Нет неявного преобразования ссылки из ‘T’ в ‘<пространство имен>. <MyBaseRecord>'»

Ответ №3:

В качестве «взлома» все записи имеют синтезированный метод <Clone>$ , который вы можете искать. Поскольку вы не можете написать метод с таким именем в C #, класс с <Clone>$ членом гарантированно будет записан начиная с C # 9.

Однако нет гарантии, что это будет продолжаться. Например, возможно, что в C # 10.0 некоторые записи не будут иметь <Clone>$ элемента или что некоторые не-записи будут.

 public static bool IsRecord(Type type) => type.GetMethod("<Clone>$") != null;
  

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

1. Я понятия не имею, зачем это делать, но теоретически с Reflection.Emit вы могли бы попросить кого-нибудь создать объект, у которого есть <Clone>$ метод, и это не запись

2. @nalka конечно, и вы также могли бы создать такой объект на другом языке — но это верно для всего, что генерирует компилятор, даже для тех, у кого есть «официальный» способ их проверки.

Ответ №4:

Как мне распознать запись с помощью отражения?

Как указано здесь и здесь

Не только нет официального способа сделать это, это явно противоречит дизайну функции. Цель для записей заключается в том, что, надеюсь, с C # 10 мы достигнем точки, когда создание записи класса a будет чисто удобным выбором, и что любая другая часть функции будет достижима с помощью некоторой формы синтаксиса. Изменение типа из записи в класс не должно быть критическим изменением, и мы даже предполагаем, что рефакторинг IDE может автоматически перемещать тип в синтаксис записи и из него без уведомления клиентов. В C # 9 есть некоторые места, где мы не совсем достигли этого, но это цель.

Несмотря на вышесказанное, все еще существуют сценарии, в которых проверка записи полезна. Некоторые хакерские способы обнаружения записей, которые работают ATM, следующие:

  1. проверьте, существует ли EqualityContract свойство с CompilerGenerated атрибутом
 isRecord = ((TypeInfo)t).DeclaredProperties.Where(x => x.Name == "EqualityContract").FirstOrDefault()?.GetMethod?.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is object;
  
  1. проверьте наличие <Clone>$ участника, как указано @Yair Halberstadt
 isRecord = t.GetMethod("<Clone>$") is object;
  

или комбинация обоих

Возможно ли иметь общее ограничение, что общий тип является записью?

НЕТ

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

1. Поиск <Clone>$ заключается в том, как привязка MVC определяет, что записи должны быть созданы. github.com/dotnet/aspnetcore/blob /…

Ответ №5:

Кажется, что это очень разумный вопрос, несмотря на некоторые ответы здесь. Реальность такова, что record — это просто синтаксический сахар над классом, и его можно создать несколькими другими способами.

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

При условии, что вы наследуете от этих классов все классы, которые вы хотите включить в ограничение, тогда это будет работать нормально.

Ответ №6:

 isRecord = t.GetMethod("<Clone>$") is object; ==> isRecord = t.GetMethod("<Clone>$") is not null;