C# 使用CSharpScript接口限制内存使用

C# 使用CSharpScript接口限制内存使用,c#,roslyn,C#,Roslyn,我有一个应用程序,其中我使用大量的CSharpScript实例来控制该应用程序。我面临的问题是,内存的使用在很大程度上取决于我使用的全局对象的类型。如果全局类型是在我的应用程序集(大型程序集)中定义的,则每个编译脚本的内存使用量将增加约100MB。如果我将全局类型放在一个单独的程序集中,我会发现每个脚本的内存使用量会增加~10MB,这对于我正在使用的脚本数量来说仍然是很大的 在使用CSharpScript API时,有没有办法限制这种内存使用?我认为这里发生的事情是,您的脚本直接引用主应用程序中

我有一个应用程序,其中我使用大量的
CSharpScript
实例来控制该应用程序。我面临的问题是,内存的使用在很大程度上取决于我使用的全局对象的类型。如果全局类型是在我的应用程序集(大型程序集)中定义的,则每个编译脚本的内存使用量将增加约100MB。如果我将全局类型放在一个单独的程序集中,我会发现每个脚本的内存使用量会增加~10MB,这对于我正在使用的脚本数量来说仍然是很大的


在使用CSharpScript API时,有没有办法限制这种内存使用?

我认为这里发生的事情是,您的脚本直接引用主应用程序中定义的对象。由于脚本被编译到一个单独的
AppDomain
,这会导致
AppDomain
从主应用程序的
AppDomain
中提取自己的本地副本。由于您有100个脚本,每个脚本在各自的
AppDomain
中,您的主程序集将被加载100次

避免这种情况的一种方法是让脚本和应用程序之间的任何接口通过一个“shim”对象,这是一个在自己的程序集中定义的小类,可以跨
AppDomain
边界“序列化”数据。该垫片类应继承自
MarshalByReferenceObject
。这可能很复杂,因为垫片无法传递应用程序中定义的任何类,否则它会像以前一样“吸入”主程序集。所有内容都必须作为.NET中定义的类传递

MarshalByReferenceObject
作为基类,允许垫片跨越域边界,而不引入程序集的整个副本。有关更多信息,请访问

我认为这里发生的是,您的脚本直接引用主应用程序中定义的对象。由于脚本被编译到一个单独的
AppDomain
,这会导致
AppDomain
从主应用程序的
AppDomain
中提取自己的本地副本。由于您有100个脚本,每个脚本在各自的
AppDomain
中,您的主程序集将被加载100次

避免这种情况的一种方法是让脚本和应用程序之间的任何接口通过一个“shim”对象,这是一个在自己的程序集中定义的小类,可以跨
AppDomain
边界“序列化”数据。该垫片类应继承自
MarshalByReferenceObject
。这可能很复杂,因为垫片无法传递应用程序中定义的任何类,否则它会像以前一样“吸入”主程序集。所有内容都必须作为.NET中定义的类传递

MarshalByReferenceObject
作为基类,允许垫片跨越域边界,而不引入程序集的整个副本。有关更多信息,请访问

我不知道您的具体实现是什么,但您可以这样做:

您可以让脚本仅为应用程序提供其逻辑,而不是使用大而重的全局对象将数据提供给脚本:

替换此csx文件,将HeavyObject作为全局:

// script.csx:
HeavyObject.DoSomeWork();
您可以编写此csx,无需全局参数:

// betterscript.csx:
public class ScriptWork : IHaveWork
{
    Work(HeavyType obj)
    {
        obj.DoSomeWork();
    }
}
return new ScriptWork();
您的应用程序中需要此界面:

public interface IHaveWork
{
    void Work(HeavyType obj);
}
脚本应该与应用程序的引用一起运行。
您将从脚本中收到IHaveWork的一个实例,然后您应该在应用程序中调用Work。

我不知道您的具体实现是什么,但您可以这样做:

您可以让脚本仅为应用程序提供其逻辑,而不是使用大而重的全局对象将数据提供给脚本:

替换此csx文件,将HeavyObject作为全局:

// script.csx:
HeavyObject.DoSomeWork();
您可以编写此csx,无需全局参数:

// betterscript.csx:
public class ScriptWork : IHaveWork
{
    Work(HeavyType obj)
    {
        obj.DoSomeWork();
    }
}
return new ScriptWork();
您的应用程序中需要此界面:

public interface IHaveWork
{
    void Work(HeavyType obj);
}
脚本应该与应用程序的引用一起运行。
您将从脚本中收到IHaveWork的一个实例,然后您应该在应用程序中调用Work。

对于某些人来说,这肯定是一个解决方案,但我需要直接设置/获取对不同全局类中许多属性的访问权。如果您要引用它们的命名空间,您应该可以从脚本访问它们的所有公共属性。这对于某些人来说确实是一个解决方案,但我需要直接设置/获取对不同全局类中的许多属性的访问。如果您要引用它们的命名空间,您应该可以从脚本访问它们的所有公共属性。我尝试过这一点,在单独的程序集中使用一个非常精简的全局类肯定会产生很大的不同,但我仍然看到每个脚本实例使用了~10MB的内存。如果您有大量脚本,这是一种改进,但在我的情况下不是解决方案。您可以通过循环查看
AppDomain.Current.assemblies
,查看是否有额外的程序集被拉入
AppDomain
。如果主应用程序中出现任何内容,则直接引用了需要删除的内容,以使其正常工作。我尝试过这种方法,在单独的程序集中使用非常精简的全局类肯定会产生很大的差异,但我仍然看到每个脚本实例的内存使用量约为10MB。如果您有大量脚本,这是一种改进,但在我的情况下不是解决方案。您可以通过循环查看
AppDomain.Current.assemblies
,查看是否有额外的程序集被拉入
AppDomain
。如果主应用程序中出现了任何内容,那么您将直接引用需要删除的内容,以使其正常工作。