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

时间:2016-04-08 15:44:50

标签: c# delegates environment-variables global

我有一些委托,两个类和一个看起来像这样的结构:

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)
   }
}

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

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

这对我来说有两个问题:

  • 我的所有值都有一个额外的参考填充。值的大小可变,但raw几乎总是8位或更少,因此差异很大,特别是因为在实际实现中,一次在内存中有数百万个值和容器是相当普遍的。
  • 无法定义正确的“null”值,因为Value必须具有环境且环境必须是可变的。这反过来意味着不将Value作为参数的Container构造函数更复杂。

我能想到的唯一另一种方法是使用包装类(环境的扩展或环境作为参数的东西),这是使用容器或值所必需的现存的容器和值作为成员。这将解决'null'问题并稍微消除Value类,但是如上所述增加了大量的开销,并为最终用户提供了一个非常复杂的接口。这些问题是,通过大量的工作和程序流程的一些变化,也可以解决,但到那时我几乎正在编写另一种编程语言,这远远超出了我的需要。

我是否还缺少任何其他解决方法,或者我错误地认为上述任何不合格因素?我唯一能想到的是,静态实现所带来的性能可能比我认为的缓存效果要小(遗憾的是我不能执行真实的基准测试 - 如何使用这个变量太多了。)< / p>

*请注意,严格来说环境不需要修改 - 从技术上讲,没有任何问题,例如

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;
    }
}

除了这对最终用户来说会更加尴尬,并且实际上并没有解决我上面提到的任何问题。

2 个答案:

答案 0 :(得分:1)

我在尝试理解你想要在这里实现的目标时遇到了很多麻烦!如果我不赞成,那就道歉了。这是一个基于单例的示例,如果我正确理解了问题,可以帮助您:

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属性会导致环境被{{1}实例化} 容器。此实例化使用ThreadLocal中的值来使用现有的Combinations方法填充环境。

答案 1 :(得分:0)

你需要让Environment成为&#34; Singleton&#34; (推荐),或将其中的所有内容标记为static。另一种可能性是使用IoC容器,但这可能比您现在准备好的更先进。

Singleton模式通常声明一个static Instance属性,该属性通过私有构造函数初始化为类的新实例。所有访问都通过static Instance属性完成,该属性将在全球范围内提供。您可以在C#here中阅读有关单身人士的更多信息。

static将允许您访问成员而无需实例化类的实例,它将充当&#34;全局&#34;容器

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。