我正在写一个库,我有一个接受字典的方法。字典的值是不受信任/不安全的,但密钥是可信的,如果最终用户能够输入任意密钥名称,则可能发生“坏事”。
因此,当其他开发人员使用此库函数时,我想强制他们在编译时知道密钥名称。所以这样的事情是允许的:
string userInput = Console.ReadLine();
Dictionary<string, string> something = new Dictionary<string, string>();
something.Add("MyKey", userInput);
因为“MyKey”是一个字符串文字,在编译时已知。但是这样的事情会引发编译或运行时异常:
string userInput = Console.ReadLine();
string userKey = Console.ReadLine();
Dictionary<string, string> something = new Dictionary<string, string>();
something.Add(userKey, userInput);
因为用户输入用于密钥(userKey),因此在编译时它是未知的。
我查看过GetType(),并没有什么能真正区分文字字符串和运行时创建的字符串。
答案 0 :(得分:3)
您可以使用string.IsInterned
来确定字符串是否被实习。默认情况下,所有编译时文字都将被实现,但您可以使用编译器标志将其关闭。
这是运行时检查,而不是编译时检查。
另请注意,可以使用string.Intern
函数实现非编译时间文字,因此技术上您可以让非文字字符串通过您的测试。但是如果你的程序要么在任何时候都没有实习字符串,要么只有你知道的实际字符串是安全的,那么这可能会有效。
另一个选项,如果你的所有密钥都应该在编译时知道,那就是没有密钥就是字符串。例如,将密钥设为枚举,以便您知道可以用作密钥的唯一值位于您在编译时修复的列表中。
答案 1 :(得分:2)
未经过广泛测试,但通过使用表达式而不是直接值,您可以测试传递的值的类型。 e.g。
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");
}
然后开发人员必须将参数作为lambda传递:
Add(() => "Test"); //Valid
string somestring = "Test";
Add(() => somestring); //Error
答案 2 :(得分:2)
我可以想到两种可能的方式,都使用反射:
接收包含键作为字符串常量字段的类型。 There is another answer on how to get constant fields from a type
定义存储字符串的属性。属性参数必须是常量表达式。您可以接收使用该属性修饰的类型或成员,并从所述属性中提取密钥。
值得一提的是,两者都可能被客户端代码伪造。例如,可以使用dynamic assemblies或System.ComponentModel.TypeDescriptor
类。