当前位置: 首页 > 知识库问答 >
问题:

编写迭代器方法时避免大结构参数的复制

彭浩穰
2023-03-14

我有一个很大的结构,我知道从剖析,是昂贵的复制。我正在使用in关键字传递这个结构的实例,效果很好。

现在我想把它作为一个参数传递给一个迭代器方法,而迭代器方法本身将值传递给其他迭代器方法--但是不允许使用in,这意味着每次将值传递给一个方法时都会复制该值。

这里的上下文是一个包含视频游戏保存状态的结构,迭代器方法是一个“加载”方法,它在Unity game engine(使用迭代器实现协同)中将保存数据的处理扩展到几个帧上。load方法比较复杂,因此需要将其分解为几种方法。

示例:

struct SaveData{
    // large data
}

// Async loading - can spread processing across frames (yay!) but copies lots of data (boo!)

IEnumerator LoadAsync(SaveData saveData) {// wish I could use 'in' here!
    // use some part of saveData
    yield return;
    // use more of saveData
    yield return InnerLoad(saveData); // wish I could use 'in' here!
}

IEnumerator InnerLoadAsync(SaveData saveData) {// wish I could use 'in' here!
    // use saveData
    yield return;
}


// Synchronous loading - very efficient (yay!) but blocks, causing an unacceptably long delay (boo!)

void LoadSynchronous(in SaveData saveData){
    // use some part of saveData
    // use more of saveData
    InnerLoadSynchronous(in saveData);
}

void InnerLoadSynchronous(in SaveData saveData){
    // use saveData
}

我理解为什么一般情况下in不允许用于迭代器(例如,如果迭代器/coroutine的持续时间超过值的所有者,该怎么办?)-所以我可以看到为什么最外层的迭代器函数需要一个副本了。但是对于内部调用,由于它们是用yield return调用的,所以内部迭代器的使用时间不会超过内部迭代器,所以似乎应该有某种方法在中使用

这里有没有我遗漏的语言特性,或者我可以使用一个很好的模式来解决它?我认为用一个外部类包装该类型是可行的,但这似乎有点混乱,当然仍然需要一个副本,因为我不能有refin成员。


共有1个答案

隆功
2023-03-14

但是对于内部调用,由于它们是用yield return调用的,内部迭代器的使用时间不会超过内部,所以似乎应该有一些方法可以使用。

你少了点什么。我们举一个简单的例子:

public class C
{
    public static void Main()
    {
        var enumerator = Outer(3);

        Console.WriteLine("Enumerating 1");
        enumerator.MoveNext();

        Console.WriteLine("Enumerating 2");
        enumerator.MoveNext();

        var innerEnumerator = (IEnumerator)enumerator.Current;

        Console.WriteLine("Enumerating Inner 1");
        innerEnumerator.MoveNext();
    }
    
    public static IEnumerator Outer(int i)
    {
        yield return null;
        Console.WriteLine("Yielding Inner");
        yield return Inner(i);
    }
    
    public static IEnumerator Inner(int i)
    {
        Console.WriteLine($"Inner {i}");
        yield break;   
    }
}

这将打印:

Enumerating 1
Enumerating 2
Yielding Inner
Enumerating Inner 1
Inner 3

(SharpLab)。

正如您所看到的,inner不是直接枚举的。编译器生成的inner实现将编译器生成的IEnumerable返回给outer的调用方,直到该调用方显式调用MoveNext时,才执行inner的主体。

但是,调用inner的时间要早得多。编译器生成的inner实现完全执行,并返回生成的IEnumerator,就在上面的产生inner之后。因此inner需要将变量i存储在编译器生成的类中的某个位置,这就是它不能是in的原因。

 类似资料:
  • Boost.Any Any库支持类型安全地存储和获取任意类型的值。当你需要一个可变的类型时,有三种可能的解决方案: 无限制的类型,如 void*. 这种方法不可能是类型安全的,应该象逃避灾难一样避免它。 可变的类型,即支持多种类型的存储和获取的类型。 支持转换的类型,如字符串类型与整数类型之间的转换。 Any实现了第二种方案,一个基于值的可变化的类型,无限可能的类型。这个库通常用于把不同类型的东西

  • 我看到的所有在Swift中定义DSL的博客帖子都使用尾随闭包,并使用闭包参数$0。这使得代码冗长,而且我认为很难看。(例如:https://mecid.github.io/2019/01/30/creating-dsl-in-swift) 有没有办法避免这样的代码? 到处都是0美元。 Kotlin通过“带接收器的扩展函数”避免了这种情况(请参阅:带T.()的Kotlin函数签名意味着什么?)。 S

  • 本文向大家介绍Java中避免NullPointerException的方法总结,包括了Java中避免NullPointerException的方法总结的使用技巧和注意事项,需要的朋友参考一下 Java中避免NullPointerException的方法总结 在字符串常量上调用equals 如果strOject == null,那下面一种方法就会抛出NullPointerException 用val

  • 问题 你在类中需要重复的定义一些执行相同逻辑的属性方法,比如进行类型检查,怎样去简化这些重复代码呢? 解决方案 考虑下一个简单的类,它的属性由属性方法包装: class Person: def __init__(self, name ,age): self.name = name self.age = age @property def n

  • 我正在使用一个由其他人编写的C++库,它(不幸的是imo)实现了几个封装在类中的算法,这些类的行为可以通过作为模板参数传递的参数(参数通常也是类)进行微调。例如,可能有这样一个类模板: 我想用这个库编写一个程序,它需要根据运行时信息创建的实例。我的问题是,假设有、和有效类型可以为、和传递,那么当我想实例化的不同spezialization时,我可能需要在代码中的某个位置创建多达分支。例如,在运行时

  • 问题内容: 我有一个包含一些键(字符串)和值(POJO)的地图 我想遍历此映射并更改POJO中的某些数据。 我继承的当前代码将删除给定的条目,并在对POJO进行一些更改后将其重新添加。 这不能很好地进行,因为在遍历地图时您不应该修改地图(方法已同步,但ConcurrentModificationException仍然出现) 我的问题是 ,如果我需要遍历地图并更改值,我可以使用的最佳实践/方法是什么