- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有 2 个程序集,A 包含 Main 方法和 Foo 类,它使用来自程序集 B 的 Bar 类:
条形组件(组件 B):
public sealed class Bar : IDisposable {
/* ... */
public void Dispose() { /* ... */ }
}
Foo 类(程序集 A):
public class Foo : IDisposable {
private readonly Bar external;
private bool disposed;
public Foo()
{
Console.WriteLine("Foo");
external = new Bar();
}
~Foo()
{
Console.WriteLine("~Foo");
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing) external.Dispose();
disposed = true;
}
}
入口点(在程序集 A 中):
class Program
{
static void Main(string[] args)
{
try
{
var foo = new Foo();
Console.WriteLine(foo);
}
catch (FileNotFoundException ex)
{
// handle exception
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
}
这个软件的要求之一是它必须优雅地处理 dll 丢失的情况。
因此,当我删除程序集 B 并启动应用程序时,我希望 main 方法中的 try catch block 处理程序集 B 丢失时抛出的 FileNotFoundException。它有点像,但这就是问题开始的地方......
当应用程序继续运行时(在控制台中输入一行),Foo 类的终结器被调用(?!),尽管没有创建 Foo 的实例 - 构造函数尚未被调用。由于没有该类的实例,因此我无法在外部实例上调用 GC.SupressFinalize。在没有 B 程序集的情况下运行项目时,您在控制台输出中看到的唯一内容是 ~Foo。
所以问题:
一些背景: 我在编写启用插件的企业应用程序时遇到了这个问题,该应用程序要求如果插件部署文件夹中缺少 dll 并标记有故障的插件,它必须继续运行。我认为围绕外部插件加载过程的 try-catch block 就足够了,但显然它不够,因为在捕获第一个异常之后,终结器仍然被调用(在 GC 线程上),这最终导致应用程序崩溃。
备注 上面的代码是我能写的最简单的代码来重现终结器中的异常。
备注 2 如果我在 Foo 构造函数中设置断点(在删除 Bar 的 dll 之后),它不会被命中。这意味着如果我在构造函数中设置一个创建关键资源的语句(在更新 Bar 之前),它将不会被执行,因此不需要调用终结器:
// in class Foo
public Foo() {
// ...
other = new OtherResource(); // this is not called when Bar's dll is missing
external = new Bar(); // runtime throws before entering the constructor
}
protected virtual void Dispose(bool disposing) {
// ...
other.Dispose(); // doesn't get called either, since I am
external.Dispose(); // invoking a method on external
// ...
}
备注3一个明显的解决方案是像下面那样实现 IDisposable,但这意味着破坏引用模式实现(即使 FxCop 也会提示)。
public abstract class DisposableBase : IDisposable {
private readonly bool constructed;
protected DisposableBase() {
constructed = true;
}
~DisposableBase() {
if(!constructed) return;
this.Dispose(false);
}
/* ... */
}
最佳答案
Why is the finalizer called even though no instance of the class is created?
这个问题毫无意义。显然创建了一个实例;如果没有创建实例,终结器将完成什么?你是想告诉我们终结器中没有“this”引用吗?
the constructor hasn't been called
无法调用构造函数,因为构建函数引用了一个缺少类型的字段。连jitt都不能调用的构造函数体怎么调用?
您似乎认为仅仅因为无法调用构造函数,就无法创建实例。这根本不符合逻辑。显然,在调用 ctor 之前 必须有一个实例,因为对该实例的引用作为“this”传递给它。所以内存管理器创建一个实例——垃圾收集器知道已经分配了内存——然后它调用构造函数。如果调用构造函数抛出异常 - 或者被异步异常中断,例如线程中止 - 那里仍然有一个实例,垃圾收集器知道,因此在它死了时需要完成。
由于对象永远不会被分配给任何事件变量——它不可能,因为分配发生在 ctor 之后,并且当 jitter 试图 jit 它时 ctor 抛出——它将被确定为死的下一代零集合。然后它将被放入终结器队列,这将使它存活。
the finalizer is still invoked (on the GC thread), which finally crashes the application.
然后修复终结器,使其不会那样做。
请记住,ctor 可以随时被异步异常(例如线程中止)中断。您不能依赖在终结器中维护的对象的任何不变量。终结器是非常奇怪的代码;您应该假设它们可以在任意线程上以任意顺序运行,对象处于任意不良状态。 您需要在终结器中编写极具防御性的代码。
If I set the breakpoint in the Foo constructor (after deleting Bar's dll) it is not hit.
正确。正如我所说,构造函数体甚至不能被 jitted。你怎么能在一个连 jitt 都无法执行的方法中设置断点?
This means if I would set have a statement in the constructor that creates a critical resource (before newing up Bar) it wouldn't be executed, hence no need for the finalizer to be called.
您是否认为需要调用终结器与垃圾收集器完全无关。终结器可能具有其他语义,而不仅仅是清理资源。垃圾收集器不会尝试从心理上确定开发人员的意图并决定是否需要调用终结器。 该对象已分配并具有终结器,并且您没有抑制其终结器,因此它将被终结。如果您不喜欢这样,那么不要创建终结器。 em> 你制作了一个终结器,因为你可能希望对象的所有实例都被终结,所以它们将会被终结。
坦率地说,我会重新审视您的基本情况。您可以安全地恢复并继续在缺少所需 DLL 的应用程序域中执行代码的想法对我来说似乎是一个非常糟糕的主意。做到这一点将非常困难。
关于c# - IDisposable with member from missing external assembly 在终结器中失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7540433/
我被告知“汇编”是您在文件中编写的内容,让您的“汇编程序”将其转换为二进制代码。 但我看到这两个术语在各种作品中混合搭配。我什至听说你编写了“汇编器”,然后“汇编器”使其可执行。 正确的用词是什么?
我在正确终止用 Assembly 编写的 16 位 DOS 程序时遇到问题。这是部分代码: .386P .model flat stack_s segment stack 'stack'
我需要多少档才能正确执行以下指令。我对我所做的事情有些困惑,所以我在这里看到专家的答案。 lw $1,0($2); beq $1,$2,Label; 请注意,检查是否会发生分支将在解码阶段完成。但是在
我正在尝试在汇编中进行简单的乘法运算,但是由于某些原因,当标记了MUL函数时,我看不到寄存器会发生变化。 mov bx, 5 mov cx, 10 mul cx 最佳答案 这些称为指令,它们指定
我正在尝试在 Assembly 中实现递归斐波那契程序。但是,我的程序崩溃了,出现了未处理的异常,我似乎无法找出问题所在。我不怀疑这涉及我对堆栈的不当使用,但我似乎无法指出哪里...... .386
我编写了以下代码: .386 .model small .stack 100h .data text db "Paper",0 .code start : lea dx ,
我有一个用汇编语言编写的裸机 ARM 的启动代码,我正在尝试了解它是如何工作的。该二进制文件被写入一些外部闪存中,并在启动时将其自身的一部分复制到 RAM 中。尽管我读过这篇文章wikipedia e
我在数据部分定义了一个二维数组和两个一维数组(一个用于列总和,一个用于行总和),并且我编写了一个函数,将二维数组求和到一维数组中。我使用 eax 和 ebx 作为二维数组的索引,但是当 eax 或 e
我正在开始组装,我正在使用 nasm 来组装代码,我正在尝试处理驻留在内存中的字符串并更改它,我想检查一个字节是否在某个范围内(ascii),这样我就可以决定如何处理它,我似乎不知道如何检查一个值是否
虽然您通常不希望将一个整体程序集用于小型项目以外的任何事情,但可能会将事物分离得太多。 组装分离过多的迹象/气味是什么? 最佳答案 第一个(明显的)是:在一个有很多项目的解决方案中,其中只有少数(比如
我正在尝试编写斐波那契的汇编代码版本,它给出第 n 个斐波那契数并返回它。 出于某种原因,它在存储斐波那契数的返回值和添加它们时遇到问题。 我希望它打印第 n 个斐波那契数。 我对我的代码做了一些修改
我有一个最小的、可重现的示例有两个问题,该示例具有三个针对 .NET Core 3.1 的项目。但我也想以 .NET Standard 2.0 为目标。 该示例适用于需要在运行时加载程序集并使用提供的
: 运算符在汇编中做什么?代码如下:DS:DX我还没有找到该运算符(operator)的任何文档。(我正在使用 NASM) 最佳答案 那实际上只是一个寄存器分隔符,而不是运算符。这意味着使用 DX 寄
我在哪里可以找到为 gmp-5.0.0 编写的程序的汇编代码我正在使用 UBUNTU 和 G++ 编译器..编译代码的命令是“g++ test.cc -o outp -lgmp” 实际上我想知道在 1
我是组装新手,我有一个关于如何表示负数的问题 我有三个 DWORDS 变量,比如说: result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式:result = i -
我想编写我的第一个汇编程序。我在论文上做了一些程序,但这是我第一次使用编译器。我正在使用 ideone .我的程序很简单, 翻译 A = 5 - A到 assembly NEG A ADD A, 5
程序集,masm 嘿,我写了宏来打印存储在 dane1 段中的 1 字节值。 我将值除以 16,然后将提醒推送到堆栈,直到值==0。然后我弹出提醒将它们转换为 ASCII 码,并打印它们。 有人可以看
我正在研究 nasm 的一个大学项目。唯一的问题是我无法生成 162 和 278 之间的偶数随机数。我尝试了很多算法,但似乎无法限制范围内的数字。 是否有一个小技巧或调整来获得所需的范围内的数字?目的
终于在无数次错误的漫长 session 之后,希望这是最后一个。 没有编译或运行时错误,只是一个逻辑错误。 编辑:(固定伪代码) 我的伪代码: first = 1; second = 1; thir
我知道在程序集r0中调用函数时,包含第一个参数,直到r3是第四个。我知道,当它超过四个时,将使用堆栈指针,但是我不太确定具体细节。 r0-r3仍然保持前四个,其余的进入堆栈吗?我正在看下面的程序集,试
我是一名优秀的程序员,十分优秀!