Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 检查字符串是否是编译时已知的文本字符串?_C#_String - Fatal编程技术网

C# 检查字符串是否是编译时已知的文本字符串?

C# 检查字符串是否是编译时已知的文本字符串?,c#,string,C#,String,我正在写一本图书馆,我有一种方法,可以读入字典。字典的值不可信/不安全,但密钥是可信的,如果最终用户能够输入任意密钥名,则可能会发生“坏事” 因此,当其他开发人员使用这个库函数时,我想强制他们在编译时知道密钥名。这样的事情是允许的: string userInput = Console.ReadLine(); Dictionary<string, string> something = new Dictionary<string, string>(); somet

我正在写一本图书馆,我有一种方法,可以读入字典。字典的值不可信/不安全,但密钥是可信的,如果最终用户能够输入任意密钥名,则可能会发生“坏事”

因此,当其他开发人员使用这个库函数时,我想强制他们在编译时知道密钥名。这样的事情是允许的:

string userInput = Console.ReadLine(); 
Dictionary<string, string> something = new Dictionary<string, string>();   
something.Add("MyKey", userInput);    
string userInput=Console.ReadLine();
Dictionary something=新字典();
添加(“MyKey”,userInput);
因为“MyKey”是一个字符串文字,在编译时就知道了。但类似这样的操作会引发编译或运行时异常:

string userInput = Console.ReadLine();
string userKey = Console.ReadLine(); 
Dictionary<string, string> something = new Dictionary<string, string>();   
something.Add(userKey, userInput);    
string userInput=Console.ReadLine();
字符串userKey=Console.ReadLine();
Dictionary something=新字典();
添加(userKey,userInput);
因为用户输入用于密钥(userKey),因此在编译时它是未知的


我查看了GetType()中的内容,没有任何东西真正区分文本字符串和运行时创建的字符串

您可以使用
string.IsInterned
来确定该字符串是否已插入。默认情况下,所有编译时文本都将被保留,但您可以使用编译器标志将其关闭

这是一个运行时检查,而不是编译时检查

还要注意,非编译时文本可以使用
string.Intern
函数进行内部存储,因此从技术上讲,可以让非文本字符串通过测试。但是,如果您的程序在任何时候都没有实习字符串,或者只实习您知道是安全的字符串,那么这可能会起作用



另一种选择是,如果您的所有键都应该在编译时已知,则根本不让键成为字符串。例如,将键设为枚举,这样您就可以知道,在编译时固定的列表中只有可以用作键的值。

未进行广泛测试,但通过使用表达式而不是直接值,您可以测试传递的值的类型。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");

}

我可以想出两种可能的方法,都使用反射:

  • 接收包含键作为字符串常量字段的类型

  • 定义存储字符串的属性。属性参数必须是常量表达式。您可以接收用该属性修饰的类型或成员,并从该属性提取密钥



值得一提的是,两者都可能被客户端代码伪造。例如,可以使用或类。

您不能创建(或重用)一个可以查看字符串值并对其进行清理或确定其是否安全的方法吗?然后保护自己不受有问题的字符串文字的影响,并允许使用非文字安全值。如果我没记错的话,属性中的表达式保证是常量。因此,您还可以尝试创建一个自定义属性,并让客户机代码向您传递一个成员或一个用该属性修饰的类型。然后,您可以从该属性读取元数据。(尽管可能有一些反射技巧可以通过使用动态程序集来伪装这一点。)问题在于密钥本身是不安全的。这就是为什么它在任何情况下都不应该来自用户输入。现在,我的选择是要么在编译时检查它是否已知,要么将它硬编码到我的库中,这样外部开发人员就无法更改它。只要您的密钥是字符串,那么任何这样做的方法都将是有漏洞的。对对象实例进行键控怎么样,它们的类型必须扩展您在库中定义的抽象基类型?实现者必须扩展该类型才能创建一个实例,然后将其传递给您作为密钥使用。我很喜欢@Theo的解决方案,这真是太好了。在运行时为lambada构造一个表达式非常容易,它返回一个
表达式。常量
返回运行时计算的字符串值。考虑到问题中的要求(并且假设客户端代码没有恶意),我认为这是最好的解决方案,既易于实现,又不太容易“意外”规避。@Servy-True,但正如您所提到的,如果开发人员有意规避限制,他可以。这仍然是一个很好的观点。增加了对实习生的额外检查,使其更加困难。不知您是否可以检查lambda是否是动态创建的。或者,您可以接收类型为
enum
的值,并将传入值的名称用作字符串。这会将字符串限制为在
enum
值的名称中有效的字符,但会允许客户端代码使用在代码中定义的任意枚举。
  Add(() => "Test"); //Valid
  string somestring = "Test";
  Add(() => somestring); //Error