0

First let me try to explain my situation.

Since we can not add a string value to an enumeration and I am NOT adding a custom parameter class in order to store and read it (for performance reasons since reflection is involved if I use something like: public class StringValueAttribute : Attribute), so I decided to use the following type of class:

public class Permissions
{
    public const string X1 = "X1";
    public const string X2 = "X2";
    public const string X3 = "X3";
    ....
    public const string X43 = "CAN_HAVE A LONG NAME AND NOT FOLLOWING A PATTERN";
}

Now I want to create a method that will receive a string as a parameter. However, I want to force the programmer to use only strings from the class Permissions.

If I would have used an enumeration, this would not be a problem since the method parameter type is explicity defined.

When we are working with generics we can implement this type of constrains. Is there anything "LIGHT/FAST" that I am not aware that allows me to do this in my case?

Dryadwoods
  • 2,875
  • 5
  • 42
  • 72
  • 1
    If the strings always look the same as the const name, why _don't_ you use an enum? When you need the string, a simple enum.ToString() would be fine...and what would your "custom parameter class" look like and why would it impact performance? – René Vogt Jul 12 '18 at 08:51
  • @RenéVogt: It sounds like the OP may be implementing an interface or similar that constrains them on the parameter type. It would be good to have more information though... – Jon Skeet Jul 12 '18 at 08:53
  • "and for performance reasons I am NOT adding a custom parameter class in order to store and read it" - I don't understand this at all. It would really help if you'd provide more context here. – Jon Skeet Jul 12 '18 at 08:53
  • You've now mentioned that "reflection is inolved" but given us no context about *how* it's involved. I've added an answer addressing your current chosen approach, but without more context we can't suggest alternative approaches which may be much better. – Jon Skeet Jul 12 '18 at 08:54
  • @Daisy Shipton, probably want to avoid this method https://blogs.msdn.microsoft.com/abhinaba/2005/10/20/c-enum-and-overriding-tostring-on-it/ – vitalygolub Jul 12 '18 at 08:58
  • @vitalygolub: I'm afraid I don't understand your comment. – Jon Skeet Jul 12 '18 at 09:00
  • @vitalygolub Exactly :) that is what I am trying to avoid. – Dryadwoods Jul 12 '18 at 09:01
  • @Dryadwoods, you can use enum Permisssions with extension method with prototype `public static string GiveMeNameMazafaka(this Permissions)` and case inside, example here https://stackoverflow.com/questions/15388072/how-to-add-extension-methods-to-enums – vitalygolub Jul 12 '18 at 09:07
  • 2
    Not using Reflection "for performance reasons" is probably not sound reasoning if your program does *any* work at all. You may want to actually put your theory to a test and profile the solution you rejected. Compared to what your program actually *does*, the overhead of reflection is minimal. – nvoigt Jul 12 '18 at 09:25

2 Answers2

1

I've been in this situation before many times. I almost always went with the struct approach.

First you define a struct like so:

public struct Permission : IEquatable<Permission>, IEquatable<string>
{
    private readonly string key;

    private Permission(string key)
    {
        this.key = key;
    }

    // implement Equals, GetHashCode, IEquatable...
}

Then you can just declare all the permissions you want to have as static members of the struct.

public class Permission
{
    // ...
    public static readonly First = new Permission("First");
}

Now, using struct as opposed to class has one major benefit and one major drawback:

  • Pro: No heap allocation needed, no a pointer dereference. It is as fast as it can be.
  • Cons: Even though we declared the constructor private structs always have an implicit default constructor. It is invisible but arrays are default initialized with it and is does default(T).

If you decide to go with a struct, it's highly advisable to add the following method/property:

public bool IsEmpty
{
    get { return this.key == null; }
}

If you decide on the class approach, the allocation is mostly saved as I don't assume you will be allocating new instances apart of the ones already declared as static members, so the drawback is really the extra pointer.

Matan Shahar
  • 3,190
  • 2
  • 20
  • 45
  • This seems to be suggesting using a different parameter type, which the OP has explicitly said can't be done. (Admittedly with relatively little context.) – Jon Skeet Jul 12 '18 at 09:28
  • @DaisyShipton I'm not sure what exactly OP means by performance reasons, I suggested struct as they do not incur performance penalty. – Matan Shahar Jul 12 '18 at 09:32
  • @DaisyShipton Yes, I said to use strings (force) but only if I would stick with that type of solution, but I never said that I am not open to alternatives. – Dryadwoods Jul 12 '18 at 09:41
  • @Dryadwoods: This is what happens when you're unclear in what you're asking. It's really hard to tell what your constraints actually are. – Jon Skeet Jul 12 '18 at 09:44
  • @DaisyShipton I really appreciate your time and help, however don't take it in a wrong way: If I don't present more details, it means that for me they are not relevant for the answer I am looking for. Matan Shahar, was able to see what I am looking for and provived an alternative, and that is all. – Dryadwoods Jul 12 '18 at 09:48
  • 2
    @Dryadwoods: The answer you've accepted doesn't answer the question you *actually asked*. That's an indication that the question doesn't represent your situation clearly, IMO - which means it's much less useful for anyone else wanting an answer to a similar question. Please try to be clearer in your requirements in the future - the question will be more valuable to other readers, and you won't waste people's time. – Jon Skeet Jul 12 '18 at 09:52
0

No, there's nothing that does this within the language. If you can possibly avoid this situation, it would be good to do so. When we have more context, we may be able to suggest alternatives.

If you really have to use this approach, I would suggest first adding execution-time validation to the method, so that if an invalid value is passed in, you'll find out immediately rather than proceeding with invalid data.

Secondly, you could write a Roslyn analyzer to find all invocations of your method, and check that the argument is of the appropriate form. However, that could cause a problem if you have a situation like this:

public void Foo()
{
    FooImpl(Permissions.X1);
    DoSomethingElse();
}

private void FooImpl(string permission)
{
    RealMethodWithChecking(permission);
}

At that point, your Roslyn analyzer would go off because the argument to RealMethodWithChecking doesn't come directly from Permissions - but it's a parameter which itself comes from Permissions. Whether or not this is a problem, and how you decide to address it, will very much depend on context.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194