Leo*_*ele 2 c# const-correctness compile-time-constant
出于此问题的目的,"常量引用"是对对象的引用,您无法从该对象调用修改对象或修改其属性的方法.
我想要这样的东西:
Const<User> user = provider.GetUser(); // Gets a constant reference to an "User" object
var name = user.GetName(); // Ok. Doesn't modify the object
user.SetName("New value"); // <- Error. Shouldn't be able to modify the object
Run Code Online (Sandbox Code Playgroud)
理想情况下,我会使用自定义属性(例如[Constant])标记不修改实例的类的每个方法,并且只能从常量引用调用这些方法.如果可能,在编译期间调用其他方法将导致错误.
我的想法是,我可以返回一个只读引用,并确保它不会被客户端修改.
您所指的技术称为" const-correctness",它是C++和Swift的语言特性,但不幸的是C#,但是您通过使用自定义属性来处理某些事情,因为这样您可以通过Roslyn强制执行它延伸 - 但这是一个兔子洞.
或者,使用接口有一个更简单的解决方案:因为C#(我认为CLR也不支持const-correctness(我们最接近的是readonly字段修饰符).NET基类库设计者添加了"只读"接口"通用可变类型,允许对象(可变或不可变)通过仅暴露不可变操作的接口公开其功能.一些例子包括IReadOnlyList<T>,IReadOnlyCollection<T>,IReadOnlyDictionary<T>-而这些都是枚举类型的技术是很好的奇异物体了.
这种设计的优点是可以使用支持接口但不支持const的任何语言.
class,struct等),或者任何不可变操作,然后创建一个不可变的接口.像这样:
假设我们有一个可变类User和一个消费服务:
public class User
{
public String UserName { get; set; }
public Byte[] PasswordHash { get; set; }
public Byte[] PasswordSalt { get; set; }
public Boolean ValidatePassword(String inputPassword)
{
Hash[] inputHash = Crypto.GetHash( inputPassword, this.PasswordSalt );
return Crypto.CompareHashes( this.PasswordHash, inputHash );
}
public void ResetSalt()
{
this.PasswordSalt = Crypto.GetRandomBytes( 16 );
}
}
public static void DoReadOnlyStuffWithUser( User user )
{
...
}
public static void WriteStuffToUser( User user )
{
...
}
Run Code Online (Sandbox Code Playgroud)
然后创建一个不可变的接口:
public interface IReadOnlyUser
{
// Note that the interfaces' properties lack setters.
String UserName { get; }
IReadOnlyList<Byte> PasswordHash { get; }
IReadOnlyList<Byte> PasswordSalt { get; }
// ValidatePassword does not mutate state so it's exposed
Boolean ValidatePassword(String inputPassword);
// But ResetSalt is not exposed because it mutates instance state
}
Run Code Online (Sandbox Code Playgroud)
然后修改你的User类和消费者:
public class User : IReadOnlyUser
{
// (same as before, except need to expose IReadOnlyList<Byte> versions of array properties:
IReadOnlyList<Byte> IReadOnlyUser.PasswordHash => this.PasswordHash;
IReadOnlyList<Byte> IReadOnlyUser.PasswordSalt => this.PasswordSalt;
}
public static void DoReadOnlyStuffWithUser( IReadOnlyUser user )
{
...
}
// This method still uses `User` instead of `IReadOnlyUser` because it mutates the instance.
public static void WriteStuffToUser( User user )
{
...
}
Run Code Online (Sandbox Code Playgroud)