Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# 9.0 determine if a type is a 'record' via reflection #3732

Closed
kofifus opened this issue Jul 27, 2020 · 20 comments
Closed

C# 9.0 determine if a type is a 'record' via reflection #3732

kofifus opened this issue Jul 27, 2020 · 20 comments

Comments

@kofifus
Copy link

kofifus commented Jul 27, 2020

Is there an official way to detrmine if a type is a 'record' via reflection ?
Looking here I was thinking about maybe detecting the EqualityContract property:

var isRecord = ((TypeInfo)typeof(MyRec)).DeclaredProperties.Any(x => x.Name=="EqualityContract");

but that feels hackish :(

@333fred
Copy link
Member

333fred commented Jul 27, 2020

There is not only not an official way to do this, it is explicitly against the design of the feature. The intent for records is that, hopefully with C# 10, we'll get to a point where making a class a record is purely a convenience choice, and that every other part of the feature will be achievable through some form of syntax. It should not be a breaking change to change a type from a record to a class, and we even imagine that an IDE refactoring could automatically move a type to and from record syntax without clients noticing. For C# 9 there are some places where we didn't quite achieve this, but that's the goal.

@kofifus
Copy link
Author

kofifus commented Jul 27, 2020

Thanks! The deeper motivation here is to know if a type is both immutable and with value semantics. This is very desirable for me (Inspired by clojure) I try to get all my types like that but is hard in C#. I have a small list of basic and core types that satisfy that and when needed use reflection to check for that etc (mostly in debug mode). Since records satisfy that (and are in fact the main example) I can include them also hence my question. (I can then use reflection to check that all members of the record are themselves immutable with value semantics etc).

@333fred
Copy link
Member

333fred commented Jul 27, 2020

That sounds more like a compile-time analyzer scenario, to flag a type if it's not immutable, rather than a record scenario. It's perfectly possible to create a mutable record, so just being a record is no guarantee of that type of behavior.

@kofifus kofifus closed this as completed Jul 28, 2020
@AartBluestoke
Copy link
Contributor

"t's perfectly possible to create a mutable record" -- sounds like that should be documented, i'm sure kofifus and I are not the only people who (without thinking too deeply) would assume that records are readonly ...

@kofifus
Copy link
Author

kofifus commented Jul 28, 2020

@AartBluestoke I think what is meant is that while all the members of a record are readonly they may be of mutable types which makes the record itself mutable. IMO C# has a real weak point with immutability but I think @333fred comment is true that an analyzer is the better way to go here

@333fred
Copy link
Member

333fred commented Jul 28, 2020

@AartBluestoke I think what is meant is that while all the members of a record are readonly they may be of mutable types which makes the record itself mutable. IMO C# has a real weak point with immutability but I think @333fred comment is true that an analyzer is the better way to go here

No, you can have setters on record properties. Records do not mean immutable. They have defaults that pull you in that direction, but they are not immutable-only types.

@kofifus
Copy link
Author

kofifus commented Jul 28, 2020

@333fred ah ok got it .. must say that is disappointing news

@MichaRotstein
Copy link

@333fred

That sounds more like a compile-time analyzer scenario

Can an analyzer identify a record ? if yes how ?

Thanks

@CyrusNajmabadi
Copy link
Member

Yes. An analyzer would be able to tell. It could just examine the syntax if the type.

@MichaRotstein
Copy link

Thanks @CyrusNajmabadi , what do you mean by "examine the syntax if the type" ?

@ufcpp
Copy link

ufcpp commented Sep 1, 2020

@kofifus
Copy link
Author

kofifus commented Sep 1, 2020

thanks @ufcpp

@0xced
Copy link

0xced commented Aug 19, 2022

I wanted to know if a type is a record to avoid serializing the EqualityContract property. In the end I checked if the property has a CompilerGeneratedAttribute. See mstum/Simplexcel#42

@Timovzl
Copy link

Timovzl commented Jun 25, 2024

Here is a use case: You're registering services for the DI container using assembly scanning, e.g. with Scrutor. You only want services in the container, not any data carriers like DTOs and value objects.

Your services probably aren't going to be records, but many data carriers will be. It's a great filter criterion to include.

@colejohnson66
Copy link

But why would you need to enforce that? If someone wants to shove a POD object into a container, why prevent them?

@julealgon
Copy link

Here is a use case: You're registering services for the DI container using assembly scanning, e.g. with Scrutor. You only want services in the container, not any data carriers like DTOs and value objects.

Your services probably aren't going to be records, but many data carriers will be. It's a great filter criterion to include.

Use a convention where the type name or namespace needs to end in Service, or create an IService marker interface that all of your service classes/interfaces implement and filter on that.

Attempting to filter "on records" would not be comprehensive enough either way to avoid the situations you are listing, since not all "non-service" classes will be records in the first place, and you'll still end up in a situation where you need to do something else to filter those out anyways.

@Timovzl
Copy link

Timovzl commented Jun 25, 2024

Oh, I wasn't implying that detecting records is a full solution to separating services from non-services. But I'm not the only one taking a heuristics-based approach, and detecting records has certainly helped with mine. I'd prefer to do it without my own extension method.

I understand that you might choose a different approach to tackle that use case. That's a trade-off concerning purely the use case. Priorities differ.

The point was to illustrate that there are reasons for developers to want to know at runtime whether a type is a record.

@CyrusNajmabadi
Copy link
Member

But I'm not the only one taking a heuristics-based approach

If you're ok with a heuristic approach, it's simple. Check for the clone method with the particular name at runtime.

@Timovzl
Copy link

Timovzl commented Jun 28, 2024

Check for the clone method with the particular name at runtime.

Yes, or the equality thing. The request is to support this with something like an IsRecord property, rather than rolling your own extension that inspects a type’s methods.

(For full clarity, heuristics referred to separating services from non-services, not evaluating whether something is a record.)

@CyrusNajmabadi
Copy link
Member

The request is to support this with something like an IsRecord property,

This exists if you use hte Roslyn API. If you want reflection to support this, it has to be a dotnet/runtime request. Roslyn doesn't control that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Tracking: Julien
Awaiting triage
Development

No branches or pull requests

10 participants