#c# #generics #c#-9.0 #record-classes
#c# #c #-9.0 #классы записей
Вопрос:
Два вопроса, касающиеся новой функции записей :
-
Как мне распознать запись с помощью отражения? глядя [здесь] [1] может быть, есть способ обнаружить
EqualityContract
, но я не уверен, что это правильный путь? -
Возможно ли иметь общее ограничение, что общий тип является записью? то есть, возможно ли указать, что параметр типа T должен быть классом записи с использованием ограничения?
Комментарии:
1. Любой псевдокод, помогающий понять проблему?
2. Почему вы спрашиваете об этом? Прежде всего, запись все еще является классом. Что касается # 2, что вы имеете в виду? Можно ли указать, что T является записью? Это всего лишь класс, поэтому, вероятно, нет
3. ©kofifus В настоящее время ведется активное обсуждение этой темы на charplang’s github под записью чемпиона Из этого комментария вы можете прочитать, что существует намерение не указывать, что класс является типом записи. В C # 10 «не будет значимой семантической разницы между записью и классом»
4. К вашему сведению, недавно была открыта проблема # 4121 , чтобы определить, является ли тип записью
5. Я думаю, что это отличный вопрос, даже если ответ «Вы не можете». Мне просто было интересно кое-что связанное: когда-нибудь не следует использовать запись вместо класса? Ответы здесь, похоже, применимы и к этому вопросу: вы не можете определить разницу, поэтому единственный раз, когда вы должны придерживаться класса, я полагаю, это если одна из встроенных функций записи (например, равенство членов) противоречит вашим намерениям.
Ответ №1:
- Как мне распознать запись с помощью отражения?
Если вы попробуете классы записей в sharplab.io
, вы увидите, что классы записей — это обычные классы, которые реализуют IEquatable<T>
интерфейс и содержат дополнительные элементы, которые используются для сравнения и клонирования экземпляров класса записей. Нет специальных атрибутов, которые указывают, что класс является record class
.
Итак, я предполагаю, что нет способа определить, является ли класс классом записи с использованием отражения.
глядя сюда, может быть, есть способ обнаружить
EqualityContract
, но я не уверен, что это правильный путь?
С помощью отражения можно определить, обладает ли класс таким свойством, но это не является 100% гарантией того, что класс с таким свойством является классом записей.
- Возможно ли иметь общее ограничение, что общий тип является записью? то есть, возможно ли указать, что параметр типа T должен быть классом записи с использованием ограничения?
Это невозможно.
- Страница предложения записей не содержит никакой информации об указании того, что параметр общего типа
T
должен быть классом записи. - Если вы прочитаете обсуждение под этим комментарием на
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, следующие:
- проверьте, существует ли
EqualityContract
свойство сCompilerGenerated
атрибутом
isRecord = ((TypeInfo)t).DeclaredProperties.Where(x => x.Name == "EqualityContract").FirstOrDefault()?.GetMethod?.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is object;
- проверьте наличие
<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;