- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我想假设这个问题的目的是检查是否至少有一种方法,即使是通过最不安全的 hack,来保持对非 blittable 值类型的引用。我知道这种设计类型堪比犯罪;除了学习之外,我不会在任何实际情况下使用它。所以现在请接受阅读异端的不安全代码。
我们知道可以通过这种方式存储和增加对 blittable 类型的引用:
unsafe class Foo
{
void* _ptr;
public void Fix(ref int value)
{
fixed (void* ptr = &value) _ptr = ptr;
}
public void Increment()
{
var pointer = (int*) _ptr;
(*pointer)++;
}
}
static class Program
{
static int _fieldValue = 42;
public static void Main(string[] args)
{
var foo = new Foo();
foo.Fix(ref _fieldValue);
foo.Increment();
}
}
static class Program
{
static Action _event;
public static void Main(string[] args)
{
MakerefTest(ref _event);
//The invocation list is empty again
var isEmpty = _event == null;
}
static void MakerefTest(ref Action multicast)
{
Action handler = () => Console.WriteLine("Hello world.");
//Assigning a handler to the delegate
multicast += handler;
//Executing the delegate's invocation list successfully
if (multicast != null) multicast();
//Encapsulating the reference in a TypedReference
var tr = __makeref(multicast);
//Removing the handler
__refvalue(tr, Action) -= handler;
}
}
__makeref
关键字,同样没有记录和建议,提供了封装和恢复对 blittable 类型的引用的可能性。但是,返回值
__makeref
,
TypedReference
,保护得很好。你不能将它存储在一个字段中,你不能装箱它,你不能创建它的数组,你不能在匿名方法或 lambdas 中使用它。我设法做的就是修改上面的代码如下:
static void* _ptr;
static void MakerefTest(ref Action multicast)
{
Action handler = () => Console.WriteLine("Hello world.");
multicast += handler;
if (multicast != null) multicast();
var tr = __makeref(multicast);
//Storing the address of the TypedReference (which is on the stack!)
//inside of _ptr;
_ptr = (void*) &tr;
//Getting the TypedReference back from the pointer:
var restoredTr = *(TypedReference*) _ptr;
__refvalue(restoredTr, Action) -= handler;
}
unsafe class Horror
{
void* _ptr;
static void Handler()
{
Console.WriteLine("Hello world.");
}
public void Fix(ref Action action)
{
action += Handler;
var tr = __makeref(action);
_ptr = (void*) &tr;
}
public void Clear()
{
var tr = *(TypedReference*) _ptr;
__refvalue(tr, Action) -= Handler;
}
}
Horror
class 是
Foo
的组合class 和上述方法,但是您肯定会注意到,它有一个大问题。在方法
Fix
,
TypedReference
tr
声明后,其地址被复制到泛型指针
_ptr
内,然后方法结束和
tr
不复存在。当
Clear
方法被调用,"new"
tr
已损坏,因为
_ptr
指向堆栈中不再是
TypedReference
的区域.那么问题来了:
TypedReference
实例存活时间不确定?
interface IRefStorage<T> : IDisposable
{
void Store(ref T value);
//IDisposable.Dispose should release the reference
}
FieldInfo
绑定(bind)字段的可能性。 ,但在我看来,后一种方法不支持派生自
Delegate
的类型。非常。
unsafe class Horror : IDisposable
{
void* _ptr;
static void Handler()
{
Console.WriteLine("Hello world.");
}
public void Fix(ref Action action)
{
action += Handler;
TypedReference tr = __makeref(action);
var mem = Marshal.AllocHGlobal(sizeof (TypedReference)); //magic
var refPtr = (TypedReference*) mem.ToPointer();
_ptr = refPtr;
*refPtr = tr;
}
public void Dispose()
{
var tr = *(TypedReference*)_ptr;
__refvalue(tr, Action) -= Handler;
Marshal.FreeHGlobal((IntPtr)_ptr);
}
}
Fix
确实是,从注释中标记为“magic”的行开始:
refPtr
作为指向 TypedReference
的指针并将其值设置为上面分配的内存区域的指针。这样做了,而不是使用 _ptr
直接,因为类型为 TypedReference*
的字段会抛出异常。 refPtr
至 void*
并将指针分配给 _ptr
. tr
作为 refPtr
指向的值因此 _ptr
. Horror
实例会“浪费”一个线程。我会借此机会重复上述方法在
否 用于现实世界的方式,这只是一个学术问题。我希望它对任何阅读这个问题的人都有帮助。
最佳答案
你到底想做什么?局部变量在堆栈上,参数也取决于调用约定。存储或返回本地或参数的地址并不好,因为它会被覆盖。除了不调用方法之外,没有办法防止它们被覆盖。
如果您打开非托管调试,您可以使用内存调试器和注册窗口来查看发生了什么。
这是更容易理解的 C 示例。为什么打印不显示正确的值。因为当打印函数被调用时,它的堆栈帧会覆盖该值。
int* bad(int x, int y)
{
int sum = x + y;
return ∑
};
int* bad2(int x, int y)
{
x += y;
return &x;
}
int _tmain(int argc, _TCHAR* argv[])
{
int* sum1 = bad(10, 10);
int* sum2 = bad(100, 100);
printf("%d bad", *sum1); // prints 200 instead of 20
sum1 = bad2(10, 10);
sum2 = bad2(100, 100);
printf("%d bad", *sum1); // prints 200 instead of 20
return 0;
};
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Xml.Linq;
using System.Runtime.InteropServices;
namespace Bad
{
class Program
{
static void Main(string[] args)
{
Action a = () => Console.WriteLine("test");
Horror h = new Horror();
h.Fix(new Big(), ref a, new Big());
h.Clear();
Console.WriteLine();
}
}
[StructLayout(LayoutKind.Sequential, Size = 4096)]
struct Big
{
}
unsafe class Horror
{
void* _ptr;
static void Handler()
{
Console.WriteLine("Hello world.");
}
public void Fix(Big big, ref Action action, Big big2)
{
action += Handler;
var tr = __makeref(action);
_ptr = (void*)&tr;
}
public void Clear()
{
var tr = *(TypedReference*)_ptr;
__refvalue(tr, Action) -= Handler;
}
}
}
关于c# - 使 TypedReference 在方法 block 之外保持事件状态而不返回它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14168002/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!