C# 模拟全局范围以包含用户定义但运行时常量的委托数组
C# simulating a global scope to contain user-defined but runtime-constant delegate arrays
我有一些委托,两个 classes 和一个看起来有点像这样的结构:
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,根据当前的Environment(与Container和Value一样,也有不同的命名,以避免与我实际代码中的 System.Environment class 冲突——我在这里使用这个名称是为了简明地暗示它的功能)。我无法解决值和通用容器的子 class 的问题,因为不同类型的值仍然需要可组合,并且容器和基值 class 都不知道什么类型的值组合应该return。
似乎无法以全局方式定义环境 class,因为现有的 System.Environment class 似乎不允许将委托存储为用户变量,并给它一个静态方法 returning 一个自身的实例将使它不可修改*,并且每次我想对 Values 做任何事情时都需要创建一个 class 的新实例,这似乎就像它应该是一个巨大的性能打击。
这给我带来了两个问题:
- 有一个额外的参考填充了我所有的价值观。值的大小是可变的,但
raw
几乎总是 8 位或更少,因此差异很大,特别是因为在实际实现中,一次在内存中拥有数百万个值和容器是相当普遍的。
- 不可能定义一个合适的 'null' 值,因为值必须有一个环境,而环境必须是可变的。这反过来意味着不将 Value 作为参数的 Container 构造函数更加复杂。
我能想到的唯一其他解决方法是使用包装器 class(环境的扩展或以环境作为参数的东西),这是与容器一起工作所必需的或值,它具有所有现存的容器和值作为成员。这将解决 'null' 问题并稍微整理一下值 class,但会增加所描述的大量开销,并为最终用户提供一个非常复杂的界面。这些问题,通过大量的工作和程序流程的一些改变,也是可以解决的,但到那时我几乎是在编写另一种编程语言,这远远超出了我的需要。
我是否遗漏了任何其他解决方法,或者我对上述任何不合格因素有误?我唯一能想到的是,静态实现对性能的影响可能比我认为的要小,这是由于缓存造成的(不幸的是,我无法执行现实的基准测试——如何使用它有太多变数)。
*请注意,严格来说,环境不需要是可修改的——从技术上讲,这不会有问题,例如,
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;
}
}
只是这对最终用户来说会更尴尬,而且实际上并没有解决我上面提到的任何问题。
您要么需要将 Environment
设为 "Singleton"(推荐),要么将其中的所有内容标记为 static
。另一种可能性是使用 IoC 容器,但这可能比您此时准备使用的更高级。
Singleton 模式通常声明一个 static
Instance
属性 并通过私有构造函数初始化为 class 的新实例。所有访问都通过 static Instance
属性 完成,这将在全球范围内可用。您可以在 C# here.
中阅读有关单例的更多信息
static
将允许您在不实例化 class 的实例的情况下访问成员,它将充当 "global" 容器。
单例示例:
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。
我在试图准确理解您在这里想要实现的目标时遇到了很多麻烦!如果我不合时宜,我深表歉意。这是一个基于单例的示例,如果我正确理解问题,可能会对您有所帮助:
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
方法来填充环境。
我有一些委托,两个 classes 和一个看起来有点像这样的结构:
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,根据当前的Environment(与Container和Value一样,也有不同的命名,以避免与我实际代码中的 System.Environment class 冲突——我在这里使用这个名称是为了简明地暗示它的功能)。我无法解决值和通用容器的子 class 的问题,因为不同类型的值仍然需要可组合,并且容器和基值 class 都不知道什么类型的值组合应该return。
似乎无法以全局方式定义环境 class,因为现有的 System.Environment class 似乎不允许将委托存储为用户变量,并给它一个静态方法 returning 一个自身的实例将使它不可修改*,并且每次我想对 Values 做任何事情时都需要创建一个 class 的新实例,这似乎就像它应该是一个巨大的性能打击。
这给我带来了两个问题:
- 有一个额外的参考填充了我所有的价值观。值的大小是可变的,但
raw
几乎总是 8 位或更少,因此差异很大,特别是因为在实际实现中,一次在内存中拥有数百万个值和容器是相当普遍的。 - 不可能定义一个合适的 'null' 值,因为值必须有一个环境,而环境必须是可变的。这反过来意味着不将 Value 作为参数的 Container 构造函数更加复杂。
我能想到的唯一其他解决方法是使用包装器 class(环境的扩展或以环境作为参数的东西),这是与容器一起工作所必需的或值,它具有所有现存的容器和值作为成员。这将解决 'null' 问题并稍微整理一下值 class,但会增加所描述的大量开销,并为最终用户提供一个非常复杂的界面。这些问题,通过大量的工作和程序流程的一些改变,也是可以解决的,但到那时我几乎是在编写另一种编程语言,这远远超出了我的需要。
我是否遗漏了任何其他解决方法,或者我对上述任何不合格因素有误?我唯一能想到的是,静态实现对性能的影响可能比我认为的要小,这是由于缓存造成的(不幸的是,我无法执行现实的基准测试——如何使用它有太多变数)。
*请注意,严格来说,环境不需要是可修改的——从技术上讲,这不会有问题,例如,
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;
}
}
只是这对最终用户来说会更尴尬,而且实际上并没有解决我上面提到的任何问题。
您要么需要将 Environment
设为 "Singleton"(推荐),要么将其中的所有内容标记为 static
。另一种可能性是使用 IoC 容器,但这可能比您此时准备使用的更高级。
Singleton 模式通常声明一个 static
Instance
属性 并通过私有构造函数初始化为 class 的新实例。所有访问都通过 static Instance
属性 完成,这将在全球范围内可用。您可以在 C# here.
static
将允许您在不实例化 class 的实例的情况下访问成员,它将充当 "global" 容器。
单例示例:
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。
我在试图准确理解您在这里想要实现的目标时遇到了很多麻烦!如果我不合时宜,我深表歉意。这是一个基于单例的示例,如果我正确理解问题,可能会对您有所帮助:
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
方法来填充环境。