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

Allow switching to static readonly properties instead of constants #64

Open
kzu opened this issue Jul 16, 2021 · 6 comments
Open

Allow switching to static readonly properties instead of constants #64

kzu opened this issue Jul 16, 2021 · 6 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@kzu
Copy link
Collaborator

kzu commented Jul 16, 2021

In some limited scenarios, it might be worth switching to static readonly properties (perhaps backed by the private constant) so that updates to the library providing the ThisAssembly don't require recompiling callers in order to get the updated values (since consts are embedded raw in the callsite at compile-time).

This might not be so common since ThisAssembly is (by default) internal and (mostly?) intended for internal consumption, but making it public via a partial class is definitely an option, which could lead to this unintended side-effect.

Perhaps introduce a new project-level property that the analyzer uses to switch the template used to generate code, like ThisAssemblyUseStaticProperties?

Originally posted by @Cologler in #63

Based on some articles:

If any package has not to recompile, we may get the wrong information. For example, if the main process tries to read the version string from a hotfix dll file, it will always read the wrong value.

@kzu kzu added enhancement New feature or request good first issue Good for newcomers labels Jul 16, 2021
@Siphonophora
Copy link

Hi. I've been looking at this and thinking through how to approach it. The main thing i'm noticing is the implementation may be cleaner with some code reuse. It looks like several of the libraries use constants so they would all needs some updates.

The shared code would primarily offer shared logic to look for the csproj property or maybe an assembly attribute [assembly: ThisAssembly.UseStaticProperties]that it would also generate. My first thought was the attribute, but I guess it really depends whether you would prefer to keep the libraries decoupled.

Also, since you mentioned the generated code being internal by default, it might be good to update the templates to reflect that. From the testing I did I mostly see no access restriction specified or public.

Let me know what you think.

@rdeago
Copy link

rdeago commented May 19, 2022

Just to clarify: should they be fields...

    public static readonly string Foo = "Bar";

...properties...

    public static string Foo => "Bar";

... or properties with backing fields?

    public static string Foo { get; } = "Bar";

Quick notes:

  • Fields should be a tad quicker to access, as there's no getter call involved. Said getter call may or may not be JITted away at runtime.
  • The readonly modifier of fields translates to metadata supported at IL level (as opposed to other, C#-specific features like init-only properties) so there's no chance of values being overwritten.
  • I don't know whether backing fields of getter-only auto properties are marked readonly, or if they can be modified (intentionally and, may I say, maliciously) via reflection.
  • Both fields and properties support the scenario described by @Cologler, unless a field is read in a loop and the compiler (either Roslyn or JIT) decides to cache its value in a temporary variable; the chances of encountering such code are rather low IMHO, but still.

Given the above, I'd go for fields; is there something I overlooked?

@viceroypenguin
Copy link
Contributor

Notes:

  • readonly fields can be written via reflection as well. Using fields instead of properties offers no security advantage for this point specifically.
  • getter-only auto properties tend to be optimized away by the JIT in most cases, since it is a simple field access and return.
  • I'd personally question the value, since as originally stated, most values generated by these libraries are for internal use and should not be publicly accessed. In fact, to that point, I'd argue that they should be marked internal rather than public for that reason. That said, just my opinion and YMMV.

@kzu
Copy link
Collaborator Author

kzu commented Nov 16, 2022

Since the auto-properties are (I'm pretty sure nearly always) optimized away, I'm tempted to say the default should be that. Perhaps the generated class visibility should determine one or the other? If it's default (internal), use fields, if it's public, use props?

And perhaps this becomes a <ThisAssemblyVisibility>public</ThisAssemblyVisibility>, say?

@viceroypenguin
Copy link
Contributor

On further reflection... I'd say if visibility is internal/default, they should be constants; if public, then they should be readonly properties.

Constants will always have (slightly) better performance behavior than properties, and cannot be changed by reflection, so if access is only available internally, then stick to constants. No one else can depend on them anyway, and any code changes will require recompile, so it works out regardless.

If public, then properties provide a (very) slight level of protection, because the property itself cannot be used to change the internal value. The value is stored in the private compiler-named backing field, so one would have to search the private fields of the class to get to it and change it. And public auto-property getter should be optimized away in most cases, so performance should be nearly perfect.

@kzu
Copy link
Collaborator Author

kzu commented Nov 16, 2022

Yep, precisely put.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

4 participants