Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.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#_Const Correctness_Compile Time Constant - Fatal编程技术网

C# 什么是恒定引用的最佳方法或替代方法?

C# 什么是恒定引用的最佳方法或替代方法?,c#,const-correctness,compile-time-constant,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. S

就本问题而言,“常量引用”是对对象的引用,您不能从中调用修改对象或修改其属性的方法

我想要这样的东西:

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
Const user=provider.GetUser();//获取对“用户”对象的常量引用
var name=user.GetName();//好啊不修改对象

user.SetName(“新值”);// 所以,这是我最初的两个想法,但并不能完全解决问题

使用动态对象:

我的第一个想法是创建一个
动态对象
,如果调用的方法没有标记为
[Constant]
自定义属性,它将拦截所有成员调用并抛出错误。这种方法是有问题的,因为a)在处理动态对象时,我们没有编译器的支持来检查代码中的错误(即方法名称拼写错误),这可能会导致很多运行时错误;b)我打算经常使用它,每次调用方法时按名称搜索方法名称可能会对性能产生相当大的影响

使用RealProxy:


我的第二个想法是使用<代码> RealPosivs/COD>来包装实际对象并验证调用的方法,但这只适用于继承自<代码> MARSALYBIOFIDENTAO/C++ >的对象。不幸的是——然而,您通过使用自定义属性来实现某些功能,因为这样您可以通过Roslyn扩展来强制实现它——但这是一个兔子洞

另外,还有一种使用接口的更简单的解决方案:因为C#(我认为CLR也不支持常量正确性(我们最接近的是
readonly
字段修饰符)..NET基类库设计者在常见的可变类型中添加了“只读接口”,以允许对象(无论是可变的还是不可变的)通过仅公开不可变操作的接口公开其功能。一些示例包括
IReadOnlyList
IReadOnlyCollection
IReadOnlyDictionary
——虽然这些都是可枚举类型,但该技术也适用于单个对象

这种设计的优点是可以使用支持接口但不支持常量正确性的任何语言

  • 对于项目中需要在不存在更改风险的情况下公开数据的每种类型(
    class
    struct
    )或任何不可变操作,请创建一个不可变接口
  • 修改消费代码以使用这些接口,而不是具体类型
  • 像这样:

    假设我们有一个可变类
    用户
    和一个消费服务:

    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 )
    {
        ...
    }
    
    然后创建一个不可变的接口:

    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
    }
    

    这就是所谓的“<代码> const <代码> >正确性”,这是C++和SWIFT的语言特征,但不是C语言,不幸的是,但是,您通过使用自定义属性来实现某些东西,因为这样可以通过Roslyn扩展实现它,但这是一个Rabut-HOLL。它实际上是不可变的。如果您将所有属性都虚拟化,可以在从同一对象继承时发出代理。在这种情况下,您实际上可以执行
    User u=prov.GetUser();//获取代理
    @T.S。被重写的
    虚拟属性无法删除属性设置程序,这意味着您不会收到编译器警告,即使被重写的设置程序抛出正确的
    NotSupportedException
    @Dai。无法删除。但OP说:“如果可能的话,在编译时调用其他方法会导致错误。”所以,我的建议是正确的。我喜欢它,因为从它继承的类发出代理并不“太复杂”。注意:接口本身不会保护不友好的调用方,因为它们可以使用反射/cast/
    dynamic
    调用实现类的
    SetXxxx
    方法。如果是这种情况,则需要同时隐藏实际类的接口和代理实现。@ AlxeLevnkkv,与C++中使用<代码> COSTOSTCAST 没有区别。无论如何,我认为不值得为敌对的库用户开发对策,因为这是为了消除潜在的bug,而不是为了安全。事实上,友好的客户端界面就足够了。目前还不清楚OP需要多少保护,“确保客户端不会修改它”可以用许多不同的方式来理解。@Alexei Levenkov我的意图正如Dai所说的,更多地是为了防止潜在的bug,而不是为了安全。对不起,没有完全弄清楚。尽管如此,观察还是很好。
    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 )
    {
        ...
    }