【问题标题】:Check if a string is a literal string known at compile time?检查字符串是否是编译时已知的文字字符串?
【发布时间】:2015-09-11 05:01:06
【问题描述】:

我正在编写一个库,并且我有一个接收字典的方法。字典的值是不受信任/不安全的,但密钥是受信任的,如果最终用户能够输入任意密钥名称,则可能会发生“坏事”。

所以当其他开发者使用这个库函数时,我想强制他们在编译时知道键名。所以这样的事情是允许的:

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() 并没有真正区分文字字符串和在运行时创建的字符串。

【问题讨论】:

  • 你不能创建(或重用)一个可以查看字符串值并对其进行清理或确定它是否安全的方法吗?然后,您可以保护自己免受有问题的字符串文字的影响,并允许使用非文字安全值。
  • 如果我没记错的话,属性中的表达式保证是不变的。因此,您还可以尝试创建自定义属性,并让客户端代码向您传递一个成员或使用该属性修饰的类型。然后,您可以从该属性中读取元数据。 (虽然可能有反射技巧可以伪造,使用动态程序集。)
  • 问题是密钥本质上是不安全的。这就是为什么它在任何情况下都不应该来自用户输入。现在我的选择是要么在编译时检查它是否已知,要么将其硬编码到我的库中,这样外部开发人员就无法更改它。
  • 只要您的密钥是字符串,您执行此操作的任何方式都将是 hacky。关闭对象实例怎么样,它们的类型必须扩展您在库中定义的抽象基本类型?实现者必须扩展该类型才能创建实例,然后将其传递给您以用作键。
  • 确实喜欢@Theo的解决方案,这该死的好。

标签: c# string


【解决方案1】:

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

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

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


如果您的所有键都应该在编译时已知,则另一个选择是根本不让键成为字符串。例如,将键设为枚举,以便您知道唯一可以用作键的值位于您在编译时修复的列表中。

【讨论】:

  • 或者,可以接收enum 类型的值并将传入值的名称用作字符串。这会将字符串限制为在 enum 值的名称中有效的字符,但允许客户端代码使用代码中定义的任意枚举。
【解决方案2】:

没有经过广泛测试,但通过使用表达式而不是直接值,您可以测试传递的值的类型。例如

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

【讨论】:

  • 在运行时构造一个表达式非常容易,它返回一个 Expression.Constant,它返回一个在运行时计算的字符串值。
  • 鉴于问题中的要求(并假设客户端代码没有恶意),我认为这是最好的解决方案,既易于实施,又不容易“意外”规避。跨度>
  • @Servy 没错,但正如您自己提到的,如果开发人员有意规避限制,他可以。还是一个好点。添加了对实习生的额外检查以使其更加困难。想知道您是否可以检查 lambda 是否是动态创建的
【解决方案3】:

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


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-10
    • 2013-12-13
    • 2013-02-13
    • 2011-02-07
    相关资源
    最近更新 更多