C#模拟全局作用域以包含用户定义但运行时常量的委托数组



我有一些委托、两个类和一个结构,看起来有点像:

delegate Value Combination(Value A, Value B);
class Environment
{
    private Combination[][] combinations;
    private string[] typenames;
    private getString[] tostrings;
    public Environment() { ... } //adds one 'Null' type at index 0 by default
    public void AddType(string name, getString tostring, Combination[] combos) { ... }
    public Value Combine(Value A, Value B)
    {
        return combinations[A.index][B.index](A, B);
    }
    public string getStringValue(Value A)
    {
        return tostrings[A.index](A);
    }
    public string getTypeString(Value A)
    {
        return typenames[A.index];
    }
}
class Container
{
    public string contents
    {
        get
        {
            return data.text;
        }
    }
    public string contentType
    {
        get
        {
            return data.type;
        }
    }
    private Value data;
    public Container(Value val)
    {
        data = val;
    }
    public Container CombineContents(Container B)
    {
        return new Container(data.Combine(B.data))
    }
}
struct Value
{
    public string type
    {
        get 
        {
            return environment.getTypeString(this);
        }
    }
    public string text
    {
        get 
        {
            return environment.getStringValue(this);
        }
    }
    public readonly int type;
    public readonly byte[] raw;
    public readonly Environment environment;
    public Value(int t, byte[] bin, Environment env)
    {
        type = t;
        raw = bin;
        environment = env;
    }
   public Value Combine(Value B)
   {
       return environment.Combine(this, B)
   }
}

之所以采用这种结构,是因为Containers可以有各种类型的Value,它们根据当前环境以用户定义的方式相互组合(与Container和Value一样,它们的名称不同,以避免与我实际代码中的System.Environment类发生冲突-我在这里使用这个名称是为了简洁地暗示它的功能)。我无法解决Value和泛型Containers的子类的问题,因为不同类型的值仍然需要组合,而且Container和基类Value都不知道应该返回什么类型的Value组合。

似乎不可能以全局的方式定义Environment类,因为现有的System.Environment类似乎不允许将委托存储为用户变量,给它一个返回自身实例的静态方法会使它不可修改*,并且每次我想用Values做任何事情时都需要创建该类的新实例,这似乎是一个巨大的性能冲击。

这给我带来了两个问题:

  • 有一个额外的引用填充了我所有的值。值的大小是可变的,但raw几乎总是8位或更少,因此差异很大,特别是因为在实际实现中,内存中同时有数百万个值和容器是很常见的
  • 不可能定义正确的"null"值,因为Value中必须有Environment,并且Environment必须是可变的。这反过来意味着,不将Value作为参数的Container构造函数要复杂得多

我能想到的唯一其他方法是拥有一个包装类(Environment的扩展或以环境为参数的东西),这是使用Containers或Values所必需的,它将所有现有的Containers和Values作为成员。这将解决"null"问题,并稍微整理Value类,但如前所述,会增加大量开销,并为最终用户带来非常复杂的界面。经过大量的工作和程序流程的一些更改,这些问题也是可以解决的,但到那时,我几乎正在编写另一种编程语言,这远远超出了我的需求。

有没有其他我遗漏的解决方法,或者我对上面的任何不合格因素都有误解?我唯一能想到的是,由于缓存,静态实现的性能打击可能比我想象的要小(不幸的是,我无法执行现实的基准测试——如何使用它有太多变量)。


*请注意,严格来说,环境不需要是可修改的——例如,从技术上讲,之类的东西不会有问题

class Environment
{
    private Combination[][] combinations;
    private string[] typenames;
    private getString[] tostrings;
    public Environment(Combination[][] combos, string[] tnames, getString[] getstrings)
    {
        combinations = combos;
        typenames = tnames;
        tostrings = getstrings;
    }
}

只是这对最终用户来说会更尴尬,而且实际上并不能解决我上面提到的任何问题。

我很难准确理解您在这里想要实现的目标!所以,如果我说错了,我很抱歉。这里有一个基于单例的例子,如果我正确理解问题,可能会对你有所帮助:

public class CombinationDefinition
{
    public string Name;
    public getString GetString;
    public Combination[] Combinations;
}
public static class CurrentEnvironment
{
    public static CombinationDefinition[] Combinations = new CombinationDefinition[0];
    public static Environment Instance { get { return _instance.Value; } }
    static ThreadLocal<Environment> _instance = new ThreadLocal<Environment>(() =>
        {
            Environment environment = new Environment();
            foreach (var combination in Combinations)
                environment.AddType(combination.Name, combination.GetString, combination.Combinations);
            return environment;
        });
    public static Value CreateValue(int t, byte[] bin)
    {
        return new Value(t, bin, Instance);
    }
}

可以用作:

CurrentEnvironment.Combinations = new CombinationDefinition[]
{
    new CombinationDefinition() { Name = "Combination1", GetString = null, Combinations = null },
    new CombinationDefinition() { Name = "Combination2", GetString = null, Combinations = null },
};
Value value = CurrentEnvironment.CreateValue(123, null);
string stringValue = CurrentEnvironment.Instance.getStringValue(value);

需要注意的是,第一次使用环境之前必须设置CurrentEnvironment.Combinations,因为第一次访问Instance属性将导致环境由其ThreadLocal容器实例化。此实例化使用Combinations中的值来使用现有的AddType方法来填充环境。

您要么需要将Environment设为"Singleton"(推荐),要么将其中的所有内容标记为static。另一种可能性是使用IoC容器,但这可能比您目前准备的更高级。

Singleton模式通常声明一个static Instance属性,该属性通过私有构造函数初始化为类的新实例。所有访问都是通过static Instance属性完成的,该属性将在全局范围内可用。你可以在这里阅读更多关于C#中的Singleton。

static将允许您在不实例化类实例的情况下访问成员,并且它将充当"全局"容器。

Singleton示例:

class Environment
{
    private static Environment _instance;
    public static Environment Instance
    {
        get
        {
             if (_instance == null)
             {
                   _instance = new Environment();
             }
             return _instance;
        }
    }
    private Environment(){}
    private Combination[][] combinations;
    private string[] typenames;
    private getString[] tostrings;
    public Environment() { ... } //adds one 'Null' type at index 0 by default
    public void AddType(string name, getString tostring, Combination[] combos) { ... }
    public Value Combine(Value A, Value B)
    {
        return combinations[A.index][B.index](A, B);
    }
    public string getStringValue(Value A)
    {
        return tostrings[A.index](A);
    }
    public string getTypeString(Value A)
    {
        return typenames[A.index];
    }
}

示例用法:

Environment.Instance.getStringValue(this);

请原谅代码中的任何语法错误,我目前无法访问Visual Studio。

最新更新