【问题标题】:Restricting use of a structure in C#在 C# 中限制结构的使用
【发布时间】:2013-02-07 16:12:12
【问题描述】:

好吧,假设我有一个这样的结构 A:

Struct A{
    private String _SomeText;
    private int _SomeValue;

    public A(String someText, int SomeValue) { /*.. set the initial values..*/ }

    public String SomeText{ get { return _SomeText; } }
    public int SomeValue{ get { return _SomeValue; } }
}

现在我想要做的是返回结构 A 作为 ABC 类中的方法的结果,如下所示:

Class ABC{
    public A getStructA(){
        //creation of Struct A
        return a;
    }
}

我不希望任何程序员使用我的库(其中将包含 Struct A 和 Class ABC 以及更多内容)能够创建 Struct A 的实例。
我希望创建它的唯一方法是从 getStructA() 方法返回。然后可以通过适当的 getter 访问这些值。

那么有没有办法设置这样的限制?所以一个结构不能在某个类之外实例化?使用 C#、.Net4.0。

感谢您的帮助。

---编辑:----
添加一些关于我为什么要实现这一目标的细节:

  • 我的 ABC 班有一些人们可以查询的“状态”。此状态有 2 个字符串值,然后是一长串整数。
  • 程序员永远不需要创建“状态”的对象/实例,状态只能由类的“getStatus()”函数返回。
  • 我不想将这 3 个字段拆分为不同的方法,因为要获得它们,我正在调用 Windows API (p/invoke),它返回包含所有 3 个字段的类似结构。
  • 如果我确实要将其拆分为 3 个方法而不使用该结构,则每次调用这 3 个方法之一时,我都必须缓存结果或从 Windows API 调用该方法...

所以我可以创建一个公共结构,程序员可以根据需要实例化它,这对他们来说是无用的,因为没有方法可以接受它作为参数。或者我可以构造这个库,使得这个结构(或者如果它使事情变得更容易的话,将它更改为一个类)只能作为方法的返回来获得。

【问题讨论】:

  • 没有。使用接口。
  • 你不能让构造函数internalprivate吗? 编辑:通过查找this 回答了我自己的问题。答案是:“是的,但没用。”
  • @Daniel 这听起来更像是一个设计问题。例如,如果您担心用户可能会在调用方法时使用他们自己的A,而不是您给他们的方法,那么您可能希望查看您的设计,而不是使用结构.请你能再解释一下这个问题的背景吗?
  • 我认为不可能对结构有这样的限制并且有一个返回相同结构的公共函数。你到底想在这里做什么?
  • 最好的办法可能是将struct 更改为class,然后声明构造函数internal

标签: c# c#-4.0 data-structures struct


【解决方案1】:

如果“受限”类型是结构,那么不,没有办法做到这一点。结构必须至少与工厂方法一样公开,如果结构是公开的,则可以使用其默认构造函数构造它。但是,您可以这样做:

public struct A
{
    private string s;
    private int i;
    internal bool valid;
    internal A(string s, int i)
    {
        this.s = s;
        this.i = i;
        this.valid = true;
    } 
    ...

现在您可以让您的库代码检查“有效”标志。 A 的实例只能由 (1) 由库内部的可调用内部构造函数的方法创建,或 (2) 由默认构造函数创建。您可以通过有效标志来区分它们。

许多人建议使用界面,但这有点毫无意义;使用结构的全部意义在于获取值类型语义,然后将其装箱到接口中。你不妨一开始就把它变成一门课。如果它要成为一个类,那么肯定可以创建一个工厂方法;只需将类的所有 ctor 设为内部即可。

当然,我希望不言而喻,这些设备都不应该用于实现能够抵抗完全受信任的用户攻击的代码。请记住,该系统旨在保护好用户免受坏代码的影响,而不是好代码免受坏用户的影响。没有什么可以阻止完全受信任的用户代码通过反射调用他们想要在您的库中的任何私有方法,或者就此而言,使用不安全代码更改结构中的位。

【讨论】:

  • 即使是不受信任的代码也可能通过滥用非原子赋值来创建带有valid==true 的结构的无效状态。使用 .net 时很容易混淆针对低信任代码的安全性和针对意外误用的安全性,因为它支持这两者并且受保护代码的差异相当微妙。
  • 在原始帖子中添加了我的使用情况等详细信息
  • @CodesInChaos:出于这个原因,我通常对试图强制执行不变量的结构的概念持怀疑态度。在我看来,结构 是一堆用胶带粘在一起的字段。虽然有时将结构伪装成不可变类对象的轻量级形式可能很有用,但这种抽象通常是有漏洞的。如果抽象是有用的(例如Decimal),那很好,但我认为在许多情况下,结构体最好明确说明其成员可能具有对其类型有效的任何值组合......跨度>
  • ...和接受结构的例程应该像单独传递它们一样验证它,而不是让结构尝试强制执行不变量。
【解决方案2】:

创建一个公共接口并使该类对调用它的类来说是私有的。

public ISpecialReturnType
{
    String SomeText{ get; }
    int SomeValue{ get; }
}

class ABC{
    public ISpecialReturnType getStructA(){
        A a = //Get a value for a;
        return a;
    }

    private struct A : ISpecialReturnType
    {
        private String _SomeText;
        private int _SomeValue;

        public A(String someText, int SomeValue) { /*.. set the initial values..*/ }

        public String SomeText{ get { return _SomeText; } }
        public int SomeValue{ get { return _SomeValue; } }
    }
}

【讨论】:

  • 当然,没有什么能阻止某人定义一个B : ISpecialReturnType,它看起来和行为都像一个A,并且可以在A 可以使用的任何地方使用(因为A 是私有的,所以你不能定义方法A 的合同,仅在 ISpecialReturnType 的条款)。不知道他的要求,就不可能知道这是否有问题。
  • 这个解决方案当然也将结构框起来。一个实现接口并且总是被装箱的结构也可能是一个类。
【解决方案3】:

您究竟关心什么?结构基本上是用胶带粘在一起的字段的集合。由于结构赋值将所有字段从一个结构实例复制到另一个结构实例,因此不受相关结构类型的控制,结构执行任何类型的不变量的能力非常有限,尤其是在多线程代码中(除非结构完全是1、2 或 4 字节,想要创建一个包含从两个不同实例复制的混合数据的实例的代码可能很容易做到,并且结构无法阻止它)。

如果您想确保您的方法不会接受除您的类型在内部生成的类型之外的任何类型的实例,您应该使用只有internalprivate 构造函数的类。如果你这样做,你可以确定你得到的是你自己生成的实例。

编辑 根据修订,我认为所要求的限制类型没有必要或特别有用。这听起来像是从根本上希望将一堆值粘在一起并将它们存储到调用者持有的一组粘在一起的变量中。如果你简单地声明一个结构:

public struct QueryResult {
  public ExecutionDuration as Timespan;
  public CompletionTime as DateTime;
  public ReturnedMessage as String;
}

然后是声明:

QueryResult foo;

将有效地创建三个变量,名为foo.ExecutionDurationfoo.CompletionTimefoo.ReturnedMessage。声明:

foo = queryPerformer.performQuery(...);

会根据函数的结果来设置这三个变量的值——本质上等价于:

{
  var temp = queryPerformer.performQuery(...);
  foo.ExecutionDuration = temp.ExecutionDuration
  foo.CompletionTime = temp.CompletionTime;
  foo.ReturnedMessage = temp.ReturnedMessage;
}

没有什么能阻止用户代码对这三个变量做任何想做的事,但那又如何呢?如果用户代码出于某种原因决定说foo.ReturnedMessage = "George";,那么foo.ReturnedMessage 将等于George。情况真的和代码说的没什么两样:

int functionResult = doSomething();

然后说functionResult = 43;functionResult 的行为与任何其他变量一样,是保存最后写入的内容。如果写入它的最后一件事是最后一次调用doSomething() 的结果,那就是它将保留的内容。如果最后写的东西是别的东西,它会包含别的东西。

请注意,与类字段或结构属性不同,结构字段只能通过写入来更改,或者使用结构赋值语句将所有字段写入一个结构实例与另一个相应字段中的值。从消费者的角度来看,只读结构属性没有这样的保证。一个结构可能碰巧实现了一个以这种方式运行的属性,但是如果不检查属性的代码,就无法知道它返回的值是否会受到某个可变对象的影响。

【讨论】:

  • 在原始帖子中添加了我的使用情况等详细信息
猜你喜欢
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 2020-10-09
  • 2012-02-25
相关资源
最近更新 更多