c# - 如果“错误 CS1628:无法在匿名方法、lambda 或查询表达式中使用 in ref 或 out 参数”,如何在线程中使用 ref 参数?
问题描述
我正在尝试编写一个补间类,为此我有一个静态Tweener.TweenTo
方法。在其中,我们启动一个线程以不阻碍外部操作。该方法如下所示:
public static void TweenTo<T>(ref ITweenable<T> obj, T target, double ms)
{
new System.Threading.Thread(() => {
System.Threading.Thread.Sleep(5000)
obj.DoStuff(5,5) //throws exception because obj is a ref parameter
}).Start()
}
我知道在 lambda 中使用 ref 参数意味着它可能是一个悬空引用,但我需要能够使用它以防用户尝试将值类型或结构传递给方法。我尝试过使用参数化线程启动,但这会将事情强制到一个我不能使用的对象(拆箱等)。我也尝试过使用一个包含指向它的指针的包装类,但这会在以后遇到并发症。
我想要一种在线程中使用此 ref 参数的方法,并理想地在其中保留其生命周期。
任何帮助表示赞赏:D
编辑:Olivier 的答案接近我的需要,但 MyClass 在某些情况下会是一个结构,并且它会尽可能地复制。这意味着它将丢失引用并从错误的实例中给出值。
编辑 2:示例结构
public struct MyStruct : ITweenable<T> {
int x;
int y;
public MyStruct(int X) {
this.x=X;
}
public void DoStuff(int newX, int newY) {
this.x=newX;
this.y=newY;
}
}
public interface ITweenable<T> {
void DoStuff(int newX, int newY);
}
编辑 3:我还没有测试过这个,所以当我有的时候我会给出答案 - 现在已经测试过,不起作用:
public static void TweenTo<T>(ITweenable<T> obj, T target, double ms)
{
Func<ITweenable<T>> getTween = ()=>{return obj;}
new System.Threading.Thread(() => {
System.Threading.Thread.Sleep(5000)
getTween().DoStuff(5,5);
}).Start()
}
解决方案
使用中间本地变量
不考虑设计和使用 ref 参数的原因,也不考虑任何线程并发和互锁管理,只需使用中间本地 var:
public static void TweenTo<T>(ref ITweenable<T> obj, T target, double ms)
{
var instance = obj;
new System.Threading.Thread(instance.DoStuff).Start();
}
测试
public interface ITweenable<T>
{
void DoStuff();
}
public class MyClass : ITweenable<int>
{
public void DoStuff()
{
Console.WriteLine("It works!");
Console.WriteLine("Press any key to exit the side thread.");
Console.ReadKey();
}
}
static private void Test()
{
var instance = (ITweenable<int>)new MyClass();
TweenTo(ref instance, 10, 20);
Console.WriteLine("Main thread ended.");
}
输出
Main thread ended.
It works!
Press any key to exit the side thread.
问题中添加的结构和代码的备注
对于提供的代码和案例,它的工作原理相同:
public interface ITweenable<T>
{
T X { get; }
T Y { get; }
void DoStuff(T newX, T newY);
}
public struct MyStruct : ITweenable<int>
{
public int X { get; private set; }
public int Y { get; private set; }
public void DoStuff(int newX, int newY)
{
Thread.Sleep(2000);
X = newX;
Y = newY;
Console.WriteLine("It works!");
}
}
public static void TweenTo<T>(ref ITweenable<T> obj, T target, double ms)
{
var instance = obj;
new System.Threading.Thread(() => instance.DoStuff((T)(object)10, (T)(object)10)).Start();
Console.WriteLine("Exiting TweenTo.");
}
测试
static private void Test()
{
var instance = (ITweenable<int>)new MyStruct();
Console.WriteLine("X is " + instance.X);
TweenTo(ref instance, 10, 20);
Console.WriteLine("Main thread ended.");
Console.WriteLine("Wait for the 'It Works' and press any key to continue main thread.");
Console.ReadKey();
Console.WriteLine("X is now " + instance.X);
Console.ReadKey();
}
输出
X is 0
Exiting TweenTo.
Main thread ended.
Wait for the 'It Works' and press any key to continue main thread.
It works!
X is now 10
推荐阅读
- ansible - 向 Ansible 中的现有键添加值
- python - 如何永久存储用户输入以在将来添加到另一个变量?
- javascript - React JS 中没有显式的数据绑定
- django - 我怎么知道电子邮件是真正的电子邮件还是不使用 django 框架
- typescript - TypeScript 隐含泛型
- javascript - 根据用户选择从数据库中获取数据
- reactjs - Firebase 存储安全规则不适用于文件夹
- mongodb - 如何使用 docker-compose 将 MongoDB 数据卷备份到主机?
- python - 如何使用python在数据框中将值从一列更改为另一列
- tsql - 如何在 TSQL / SQL Server Express 中创建列 AUTOINC?