2

I am working with a third party control that is using "strange values" to distinguish between a list of choices. They use two different properties to uniquely identify each choice.

Example:
"Field" + "RW" = "CheckedOutBy"
"System" + "N" = "Name"
"Field + "N" = "Notifier"

There are a total of 37 different choices in all (each with a different combination of the two values to make up the 37 unique choices).

I have created a struct that stores the two values, with the idea that I will create a new instance of the struct for each choice.

public struct ColumnCode : IEquatable<ColumnCode>
{
    public static readonly ColumnCode Empty = new ColumnCode();

    private readonly ColumnType _columnType;
    private readonly string _code;

    internal ColumnCode(ColumnType columnType, string code)
    {
        _columnType = columnType;
        _code = code;
    }

    public override string ToString() { ... }
    public bool Equals(ColumnCode other) { ... }
    public override int GetHashCode() { ... }
}

Ideally I would like to create a "constant" for each of the choices, but since constants are not an option I want to try and mimic a constant.

The two approaches I have come up with are to either use a static readonly field or a static property with only a getter.

public static class FieldOption 
{
    public static ColumnCode CheckedOutBy { get; } = new ColumnCode(ColumnType.Field, "XW");
    public static ColumnCode Name { get; } = new ColumnCode(ColumnType.System, "N");
    public static ColumnCode Notifier { get; } = new ColumnCode(ColumnType.Field, "N");
}

or

public static class FieldOption 
{
    public static readonly ColumnCode CheckedOutBy = new ColumnCode(ColumnType.Field, "XW");
    public static readonly ColumnCode Name= new ColumnCode(ColumnType.System, "N");
    public static readonly ColumnCode Notifier = new ColumnCode(ColumnType.Field, "N");
}

In either case I can now refer to the choices in my C# code using FieldOption.CheckedOutBy, FieldOption.Name or FieldOption.Notifier, etc but I am not sure if one approach is better than the other.

Is one of these choices better than the other to mimic a const, or is there a better approach that I am not considering.

I have read a large amount of information on the internet and am still not coming up with a good answer. Some of it seems to be conflicting. Much of the information states to favor properties over fields but then in this article (https://msdn.microsoft.com/en-us/library/ms229057(v=vs.110).aspx) Microsoft says "DO use public static readonly fields for predefined object instances" so I feel static readonly fields is the correct choice.

I am also not sure how reflection falls into play here. I want to make sure that the values for the FieldOptions cannot be changed, even through reflection.

Any help on this would be greatly appreciated.

Gene S
  • 2,735
  • 3
  • 25
  • 35
  • Do you know what the keywords do? – Jeroen Vannevel Jun 21 '16 at 22:16
  • @Jeroen Vannevel Not sure I understand your question? I am just wondering if the C# compiler handles these differently? Is there performance advantages, etc. – Gene S Jun 21 '16 at 22:19
  • One is static, the other isn't. One is a property, the other is a field. Evidently these will have differences -- it's up to you to decide what kind of differences you're interested in. If you mean *all* differences then the best course of action is to read the documentation and see what the effect is of `static` and how a field and a property differ. Once you've formulated a more specific question, we can answer that instead – Jeroen Vannevel Jun 21 '16 at 22:21
  • So far there is no indication in the post that problem is more specific than regular "public field vs property" topic I linked as duplicate... I'm sure you've read through it carefully before asking the question, but it did not get reflected in the post - please make sure edit your post if you feel it need to be re-opened. – Alexei Levenkov Jun 21 '16 at 22:23
  • @AlexeiLevenkov If I'm understanding this question properly, I think it can definitely stand alone from the duplicate target, specifically if it were reworded to ask specifically about the performance aspect. Primarily because the answer to the target is to use properties which would not be the case here if performance was the main concern. –  Jun 21 '16 at 22:57
  • 1
    @Phaeze performance question should contain measurements, perf goals and concrete question about what author expect to see explained/improved. This is also in addition to regular requirement to show research (especially on questions discussed to death like "whether property access get inlined")... So I doubt that OP actually asking about that (as none of required info shown in the post) – Alexei Levenkov Jun 21 '16 at 23:05
  • @AlexeiLevenkov Fair enough, and as you allude to if that were the case it would simply need a different dupe target. –  Jun 21 '16 at 23:10
  • @AlexeiLevenkov I see now why my original question was not correctly asked. I have added more detail as to exactly what I trying to accomplish. Is this more appropriate or would it still be considered a duplicate? – Gene S Jun 21 '16 at 23:31
  • Question looks much better now (very concrete and definitely not duplicate of one I picked - reopened)... Also I'm afraid you are explicitly excluding "field" option with reflection requirement. – Alexei Levenkov Jun 21 '16 at 23:46

1 Answers1

2

I want to make sure that the values for the FieldOptions cannot be changed, even through reflection

The only way to accomplish that would be to use read-only properties, and even then only if you reinstantiate the respective value every time the getter is called, e.g.:

public static ColumnCode CheckedOutBy
{
    get { return new ColumnCode(ColumnType.Field, "RW"); }
}

Using the syntax you've shown, or auto-properties with a private setter, there is still a backing field and that backing field can still be changed through reflection, albeit with slightly more difficulty since the field name is not visible in the source code and has a compiler-implementation-dependent name.

Of course, reinstantiating the value every time the getter is called has negative performance implication. So there's a cost to making the property deeply read-only (i.e. immune to reflection). Whether this performance cost matters in your scenario is something you'll have to determine for yourself. There's no "one right answer" to that question.

I have read a large amount of information on the internet and am still not coming up with a good answer. Some of it seems to be conflicting

That's because, other than scenarios where you have some specific constraint that informs your decision (such as wanting to prevent reflection from being able to change the value), this really is mostly a matter of personal preference. The previously proposed duplicate answer addresses many of the issues that differentiate fields from properties, but as a practical matter in the specific scenario you're asking about, there's very little real difference.

Fields nominally perform better, since they can be accessed directly instead of requiring a method call. But a) in many cases, the method's body will be inlined, negating that advantage, and b) even if the method call exists, it's not really enough overhead to matter or even be measurable in most cases.


So, you need to decide: how badly do you need protection against code modifying these values via reflection? Is it really important enough to make it worth wrapping the values in properties with procedurally-generated values? If you do so, how often will you be accessing these properties? Will it be frequent enough that the overhead of generating the values on-demand every time the getter is called will impact the usefulness of your code?

These are questions only you can answer. It is not possible for someone here to answer that for you.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • Even with the property getter, someone running the code with full privileges can simply edit the method pointer for that member, changing the code run when that property getter is called. Fundamentally there's nothing *at all* you can do to stop someone running arbitrary code with full admin rights from doing on that machine, especially with respect to it's own application code. – Servy Jun 22 '16 at 03:38
  • @Servy: yes, that's true. I consider that a whole other level of hacking though. .NET provides all the tools needed to _easily_ interfere with the value of a field, and so it's worth keeping in mind the difference between protecting against that or not. There's no point in worrying about the attacker who you can't stop. – Peter Duniho Jun 22 '16 at 03:44
  • 1
    You can't really argue that someone's going to "accidentally" go and use reflection to modify the value of a private backing field without realizing that they weren't intended to do so, and a determined malicious user running full privileged code is already a lost cause. There's really nothing in between those two. – Servy Jun 22 '16 at 04:38
  • @Servy: I've tried to respond to your comment in good faith, but you are putting words in my mouth and arguing a point I never made. I don't know what motivates your tirades, but I'm not interested in playing this game. – Peter Duniho Jun 22 '16 at 04:43
  • So then what is your point? How have I misunderstood it? Are you arguing that people that will go and use reflection to modify the value of a private anonymous backing field aren't maliciously violating the expectations of the code? I don't think I misunderstood your assertion that a user attempting to do..just about anything for their own system is going to be *able* to do what they want; you couldn't stop them from doing anything to it. – Servy Jun 22 '16 at 13:06
  • 1
    They're knowingly violating a constraint of the code. It's clear to them that they're not supposed to change the value, and they're doing it anyway. When doing so, they need to then be prepared for the consequences of violating the clear expectations of that library. If the result of the change is that the code doesn't work, they broke their own code, no reason to stop them. If it allows them to do something that it's important they not do, then you've not going to be able to stop them no matter *what* you do. If they only want to break their own program, let them. – Servy Jun 22 '16 at 15:46
  • 1
    Yes, exactly. I don't see how using reflection to set a private anonymous backing field would be "accidentally" or "carelessly" doing something that you thought the author of that code wanted you to be able to do. `readonly`, as you have just said, is a great example of that. You can set a `readonly` field if you're trying to, but because it's marked as "read only" you know you're not supposed to, so you shouldn't if you want it to work. LIkewise, if a property has a getter, and no (public) setter, you know you're not supposed to be able to set that value publicly. – Servy Jun 22 '16 at 16:04
  • True enough. What is highly relevant however is that your "safe" solution can still be edited through reflection; it's just harder to do. The level of protection he's asking for is provably unachievable, under all circumstances. If you want to only address how to prevent the behavior from ever being changed, there is no answer other than, "you can't". You instead chose to focus your answer on trying to draw a line of what was "hard enough" and what wasn't, contrary to the literal question asked, as you yourself have described it. – Servy Jun 22 '16 at 16:16
  • 1
    So you're saying that your opinion over how far it's practical to go to stop people from doing things that could harm them is relevant, but nobody else's opinion is relevant because that's not what the question is actually asking? If you're going to not answer the question at all, and just give your opinion on what you think he should do (regardless of what he's asking) then why are you opposed to others discussing the appropriateness of that action? – Servy Jun 22 '16 at 16:37
  • 1
    As for your analogy, it doesn't really apply here. Locking your door is nothing more than informing someone who sees it that you'd rather they didn't come in your house. You're not stopping anyone from actually coming in. That is analogous to using a property with just a getter. You're clearly informing the user of the code that they are only supposed to read the value, and they need to do something they know they're not supposed to do in order to try to set it. How hard it is to set a field isn't really the issue here, it's whether they could set it without realizing they shouldn't. – Servy Jun 22 '16 at 16:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/115332/discussion-between-peter-duniho-and-servy). – Peter Duniho Jun 22 '16 at 16:44
  • @PeterDuniho Thanks Peter. This helps me tremendously. I wanted to mimic a constant as close as possible but you have helped me to realize the cost is not worth it. Like you state, it will be difficult, if not impossible, to *accidently* make changes using reflection and for me the prevention of accidental or inadvertent changes is most important. Given that I can't 100% mimic a constant then using Fields will get me what I really want. Thanks for your great explanation and helping me wrap my mind around all this. – Gene S Jun 23 '16 at 17:27