考虑C#中的"一次性"关键字

Pet*_*old 13 c# language-features design-patterns idisposable

您对如何在.Net中实施一次性物品有何看法?你如何解决实现IDisposable类的重复性?

我觉得IDisposable类型不是他们应该去过的一流公民.太多是由开发人员的怜悯.

具体来说,我想知道是否应该在语言和工具方面提供更好的支持,以确保一次性物品的正确实施和妥善处理.

例如,在C#中,如果需要实现一次性语义的类可以像这样声明:

public class disposable MyDisposableThing
{
    ~MyDisposableThing()
    {
        // Dispose managed resources
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器可以轻松生成IDisposable接口的实现.析构函数~MyDisposableThing可以转换为应该释放托管资源的实际Dispose方法.

中间C#代码如下所示:

public class MyDisposableThing : IDisposable
{
    private void MyDisposableThingDestructor()
    {
        // Dispose my managed resources
    }

    ~MyDisposableThing()
    {
        DisposeMe(false);
    }

    public void Dispose()
    {
        DisposeMe(true);
        GC.SuppressFinalize(this);
    }

    private bool _disposed;
    private void DisposeMe(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // Call the userdefined "destructor" 
                MyDisposableThingDestructor();
            }
        }
        _disposed = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样可以实现更清晰的代码,更少的样板处理代码以及处理托管资源的一致方式.对于边缘情况和非托管资源,仍然支持手动实现IDisposable.

确保实例得到妥善处理是另一项挑战.请考虑以下代码:

private string ReadFile(string filename)
{
    var reader = new StreamReader();
    return reader.ReadToEnd(filename);
}
Run Code Online (Sandbox Code Playgroud)

reader变量永远不会超出方法的范围,但必须等待GC处理它.在这种情况下,编译器可能会引发StreamReader对象未显式处理的错误.此错误将提示开发人员将其包装在using语句中:

private string ReadFile(string filename)
{
    using (var reader = new StreamReader())
    {
        return reader.ReadToEnd(filename);
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 24

一个常用的原则是"需要设计模式来解决语言缺陷".这是该原则的一个例子.我们需要一次性模式,因为语言不会给你.

我同意,可处置可能已经抬高了"模式"的世界,进入C#语言正确的,因为我们有,比如说,财产getter和setter方法(这是具有getter和setter方法的模式的标准化),或事件做(它标准化了存储委托的想法,并在有趣的事情发生时调用它.)

但是语言设计很昂贵,并且可以应用有限的努力.因此,我们试图找到最有用,最引人注目的模式,以适当的语言.我们试图找到一种方式,这种方式不仅方便,而且实际上为语言增添了更多的表达能力.例如,LINQ将过滤,投影,连接,分组和排序数据的概念转换为适当的语言,这为语言增添了许多表达能力.

虽然这当然是一个好主意,但我认为它不符合标准.我同意这将是一个很好的方便,但它不会启用任何真正丰富的新方案.

  • 再次,autoproperties的例子是IMO仅仅是方便.然而它的有用之处在于它解决了不便和代码可读性的问题.提升一次性模式不仅可以解决不便和代码混乱,还可以帮助开发人员做正确的事情.当然,它不会带来任何新的丰富场景,它将使代码更具表现力和更安全,这至少是一个有价值的场景. (6认同)
  • 我很惊讶Eric对autoprops感到惊讶:)他们所做的不同之处在于它实际上很容易实现属性(而不是字段),甚至懒惰的程序员也会在应该使用它们的地方使用它们.这是一个巨大的差异,IMO值得所有的实施工作.这个提议会让懒惰的程序员正确编写一次性类并妥善处理它们. (6认同)
  • 实际上,autoprops几乎只是一种便利.我对他们进入C#3.0感到非常惊讶.汽车制造商的设计,实施,测试和文件成本低得足以证明其合理性,但仍然令人惊讶. (4认同)
  • @Camilo:增加语言复杂性是*许多人抱怨的事情.每次添加新功能时,我们会收到很多很多投诉,因为每个新功能都意味着花更多的钱来培训人们如何使用新功能.通过元编程增加可扩展性进一步加剧了这个问题; 现在你必须训练人们不仅仅是如何读写C#,而是如何在其中读取和编写带有元编程的C#.也就是说,我对元编程非常感兴趣,并会考虑将某种形式添加到C#的假设未来版本中. (2认同)

Gre*_*ech 6

除了其他答案之外,还有一个问题是这应该实现多少以及人们应该从中得到什么?说我像这样宣布我的课:

public disposable class MyClass
{
    readonly AnotherDisposableObject resource = new AnotherDisposableObject();

    ~MyClass()
    {
        this.resource.Dispose();
    }

    public void DoStuff()
    {
        this.resource.SomeMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

那么如果在实例DoStuff 调用的调用者被处理掉,你会发生什么?应该编译器自动插入类似的东西

if (this.disposed) throw new ObjectDisposedException();
Run Code Online (Sandbox Code Playgroud)

在每个方法的开头,因为你已经宣布该类是一次性的?

如果是这样,那么在处理对象之后显式允许调用方法的情况怎么样(例如MemoryStream.GetBuffer)?你是否必须引入一个新的关键字,向编译器表明这一点,例如public useafterdispose void ...

如果没有,那么如何向人们解释新关键字为您实现一些样板代码,但是他们仍然需要编写代码来检查对象是否在每个方法中处理?而且,他们怎么检查这一点,因为关于对象是否已被处置的所有状态信息都是自动生成的!现在,他们需要在~MyClass方法中跟踪自己的标志,这将取消编译器应该为您完成的一半工作.

我认为作为一种特定的语言模式,概念中存在太多漏洞,并且它只试图解决一个特定问题.现在,能够以通用方式解决这一整类问题的是mixins(即一次性mixin),这种语言特征通常可以重用于不同的概念(例如Equatable mixin,Comparable mixin等).那是我的钱去的地方.


Nol*_*rin 5

就个人而言,我认为IDisposable在当前版本的.NET中支持相当不错.using关键字的存在几乎使它成为我的第一类构造.

我承认有一定数量的样板代码,但不足以保证新的语言功能.(自动实现的属性是一个很好的例子,这个功能正在被引入.)你错过了帖子中的一个重点,即这个"样板"代码并不总是你所需要的.主要是,您需要在块外部处理非托管资源if (disposing).

当然,析构函数(~MyDisposableThing)和无参数Dispose()方法是真正的样板,并且可以被语言关键字的用户消除,正如您所建议的那样 - 但是我再次确定引入一个实际的新关键字对于少数人来说是必要的.代码行.

我当然看到你在这里提出的观点,并在某种程度上同情它.(我确信如果你的建议成为语言规范的一部分,那么编码人员就不会抱怨.)然而,当代码行数相当有限时,它不可能说服.NET开发团队,其中一些可以说是有争议的相当特定于上下文(因此不是样板).