Check if a string is a literal string known at compile time?

I am writing a library and I have a method that uses a dictionary. The value of the dictionary is unreliable / unsafe, but the key is trusted, and if the end user was given the opportunity to enter an arbitrary key name, then "bad things" can happen.

Therefore, when other developers use this library function, I want to make them know the key name at compile time. So this is allowed:

string userInput = Console.ReadLine(); Dictionary<string, string> something = new Dictionary<string, string>(); something.Add("MyKey", userInput); 

Because "MyKey" is a string literal known at compile time. But something like this can cause either compilation or a runtime exception:

 string userInput = Console.ReadLine(); string userKey = Console.ReadLine(); Dictionary<string, string> something = new Dictionary<string, string>(); something.Add(userKey, userInput); 

Since user input was used for the key (userKey), and thus it was unknown at compile time.

I looked in GetType (), and there is nothing that really distinguishes a literal string and a string created at runtime.

+5
source share
3 answers

I can imagine two possible ways using reflection:

  • Get the type containing the key as a string constant field. There is another answer on how to get constant fields from a type .

  • Define the attribute that stores the string. Attribute arguments must be constant expressions. You can get the type or element decorated with this attribute and extract the key from the specified attribute.


It is worth mentioning that both can be faked with client code. For example, one could use dynamic assemblies or System.ComponentModel.TypeDescriptor .

+2
source

You can use string.IsInterned to determine if a string is interned. All compilation time literals will be interned by default, but you can disable this using the compiler flag.

This is a runtime check, not a compile time check.

Also note that time literals other than compilation can be interned using the string.Intern function, so technically you can pass non-literal strings. But if your program either doesn't intern the strings at any time, or just the ever interned strings that you know are safe, then this might work.


Another option, if all keys should be known at compile time, is that the key should not be a string at all. Make the key, for example, an enumeration, so that you know that the only values ​​that can ever be used as keys are in the list that you corrected at compile time.

+2
source

Not tested much, but using expressions instead of direct values ​​you can check the type of the transmitted value. eg.

 void Add(Expression<Func<string>> fn) { if (fn.Body.NodeType != ExpressionType.Constant) throw new ArgumentException("Only literal strings are allowed"); //and extra check if the value itself is interned var val = fn.Compile()(); if (string.IsInterned(val) == null) throw new ArgumentException("Only literal strings are allowed"); } 

Then the developer should pass the argument as a lambda:

  Add(() => "Test"); //Valid string somestring = "Test"; Add(() => somestring); //Error 
+1
source

All Articles