C# 如何对充满常量的类使用泛型?

C# 如何对充满常量的类使用泛型?,c#,generics,C#,Generics,我希望我的意思变得清楚。我有多个充满选项的静态类: static class Thing1 { public const string Name = "Thing 1"; // More const fields here. } static class Thing2 { public const string Name = "Thing 2"; // More const fields here. } 现在我想使用这些选项来创建一个类,其中包括这些类中的一个

我希望我的意思变得清楚。我有多个充满选项的静态类:

static class Thing1
{
    public const string Name = "Thing 1";
    // More const fields here.
}

static class Thing2
{
    public const string Name = "Thing 2";
    // More const fields here.
}
现在我想使用这些选项来创建一个类,其中包括这些类中的一个的内容

public void Create<T>()
{
    var foo = new Foo(T.Name);
    foo.Prop = T.Something;

    if (T.HasValue)
        foo.Add(T.Value);
}
public void Create()
{
var foo=新foo(T.Name);
foo.Prop=T.某物;
if(T.HasValue)
foo.Add(T.Value);
}
但这当然行不通。我会使用接口,但静态类不能实现接口

有什么方法可以使这项工作优雅地进行吗?制作
Thing1
Thing2
单例可以,但这不是一个很好的解决方案


我可以创建一个结构并将对象放入另一个静态类中,但我想知道您是否可以执行上述操作。

而不是使用常量的静态类。可以创建具有属性的类和具有所需值的静态实例

public class Thing
{
    private Thing(string name, string something, bool hasValue, string value)
    {
        Name = name;
        Something = something;
        HasValue = hasValue;
        Value = value;
    }

    public string Name { get; }
    public string Something{ get; }
    public bool HasValue { get; }
    public string Value{ get; }

    public static Thing Thing1 { get; } = new Thing("Thing1", "Something1", true, "Value1");
    public static Thing Thing2 { get; } = new Thing("Thing2", "Something2", false, null);
}
然后你的方法就可以上这个课了

public void Create(Thing t)
{
    var foo = new Foo(t.Name);
    foo.Prop = t.Something;

    if (t.HasValue)
        foo.Add(t.Value);
}
那你也可以叫它

Create(Thing.Thing1);


您可以创建一个接口,使您的类成为非静态类,并从该接口继承:

public class Thing1 : IThing
{
    public string Name { get; } = "Thing 1";
    // More const fields here.
}

public class Thing2 : IThing
{
    public string Name { get; } = "Thing 2";
    // More fields here.
}

interface IThing 
{   
    string Name { get; }
}
然后将其与类型参数约束一起用于方法:

public void Create<T>(T t) where T : IThing
{
    // Now compiler knows that `T` has all properties from `IThing`
    var foo = new Foo(t.Name);
    foo.Prop = t.Something;

    if (t.HasValue)
        foo.Add(t.Value);
}
public void Create(T)其中T:it
{
//现在编译器知道'T'具有来自'it'的所有属性`
var foo=新foo(t.Name);
foo.Prop=t.某物;
if(t.HasValue)
foo.Add(t.Value);
}
您可以尝试反射:扫描程序集以查找
静态
类,获取
公共常量字符串
字段及其值,并将其具体化为
字典

使用System.Linq;
运用系统反思;
...
//关键字:type+字段名,比如Tuple.Create(typeof(Thing1),“name”)
//值:对应值,表示“物1”;
静态字典s_Dictionary=AppDomain
.CurrentDomain
.getAssemblys()//我已获取所有程序集;您可能需要在此处添加位置
.SelectMany(asm=>asm.GetTypes())
.Where(t=>t.isastract&&t.IsSealed)//所有静态类型,您可能需要添加Where
.SelectMany(t=>t
.GetFields()//所有常量字符串字段
.其中(f=>f.FieldType==typeof(字符串))
.其中(f=>f.IsPublic和f.IsStatic)
.Where(f=>f.IsLiteral&!f.IsInitOnly)//仅限常量
.选择(f=>new{
key=Tuple.Create(t,f.Name),
value=f.GetValue(null)
}))
.ToDictionary(item=>item.key,item=>item.value?.ToString());
如果您不希望扫描所有已加载的程序集,而是只扫描一个(正在执行的)程序集

静态字典s\u Dictionary=Assembly
.getExecutionGassembly()
.GetTypes()
.其中(t=>t.isastract和&t.IsSealed)
...
现在你可以把字典包装起来,比如说

  public static string ReadConstant<T>(string name = null) {
    if (string.IsNullOrEmpty(name))
      name = "Name";

    if (s_Dictionary.TryGetValue(Tuple.Create(typeof(T), name), out string value))
      return value;
    else
      return null; // Or throw exception
  }
publicstaticstringreadconstant(stringname=null){
if(string.IsNullOrEmpty(name))
name=“name”;
if(s_Dictionary.TryGetValue(Tuple.Create(typeof(T),name),out字符串值))
返回值;
其他的
返回null;//或引发异常
}
用法

string name1=ReadConstant();

您可以使用非静态类,并将它们添加到具有
类型
键的字典中。但您必须使用只读属性

public interface IConstants
{
    string Name { get; }
    double InitialHealth { get; }
    public int? MaxTries { get; }
}

public class Thing1 : IConstants
{
    public string Name => "Thing 1";
    public double InitialHealth => 100.0;
    public int? MaxTries => null;
}

public class Thing2 : IConstants
{
    public string Name => "Thing 2";
    public double InitialHealth => 80.0;
    public int? MaxTries => 10;
}
初始化字典:

public static readonly Dictionary<Type, IConstants> Constants =
    new Dictionary<Type, IConstants> {
        [typeof(Thing1)] = new Thing1(),
        [typeof(Thing2)] = new Thing2(),
    };

经过深思熟虑,我想出了另一个解决办法。为什么要按类型选择常量?如果我们使用相同的类型来存储不同的常量集,那么就容易多了

public class Constants
{
    public string Name { get; set; }
    public double Health { get; set; }
    public int? MaxTries { get; set; }
}
然后,我们通过
枚举
识别集合:

public enum SetType
{
    Set1, // Please use speaking names in a real implementation!
    Set2,
    Set3
}
我们在创建常量集字典时定义常量值:

public static readonly Dictionary<SetType, Constants> ConstantSets =
    new Dictionary<SetType, Constants> {
        [SetType.Set1] = new Constants { Name = "Set 1", Health = 100, MaxTries = null },
        [SetType.Set2] = new Constants { Name = "Set 2", Health = 80, MaxTries = 5 },
        ...
    };

没有泛型,没有反射,不需要花哨的东西。

你不能同时使用泛型和静态,这是一个或另一个。@gunr217所以没有办法做到这一点?真遗憾。你可以让它工作,只是不能使用静态。你能给出一个更具体的例子来说明你为什么要这样做吗?@gunr217这是一个游戏,静态类是实体的属性,
Create
函数将创建具有指定属性的实际游戏实体。我的意思是对于泛型,不,不可能。您仍然可以使用反射。您可能希望将
readonly
添加到静态字段
Thing1
Thing2
。使用工厂方法可以创建Thing对象并初始化所有属性。如果Thing1假设要初始化的属性超过5个(代码气味),那么构造函数参数的数量可能会变得难看。@Vendettami在同一个注释中谈论代码气味并推荐工厂,这有点讽刺!如果您想分享一些见解,@20只是对属性的一般偏好,而不是公开字段。我想这会带来相当大的运行时成本,对吗?@Rakete1111:它只运行一次(有了字典,您可以立即得到答案);很可能我们不希望将所有加载的程序集扫描为
AppDomain.CurrentDomain.GetAssembly().SelectMany(asm=>asm.GetTypes())…
,但只扫描一个,比如说
Assembly.GetExecutionGassembly().GetTypes()…
public static readonly Dictionary<Type, IConstants> Constants =
    new Dictionary<Type, IConstants> {
        [typeof(Thing1)] = new Thing1(),
        [typeof(Thing2)] = new Thing2(),
    };
public void Create<T>()
{
    Type key = typeof(T);
    var foo = new Foo(key.Name);
    IConstants constants = Constants[key];
    foo.InitialHealth = constants.InitialHealth;

    if (constants.MaxTries is int maxTries) { // Only true if MaxTries.HasValue.
                                                // Converts to int at the same time.
        foo.Add(maxTries);
    }
}
public class Constants
{
    public string Name { get; set; }
    public double Health { get; set; }
    public int? MaxTries { get; set; }
}
public enum SetType
{
    Set1, // Please use speaking names in a real implementation!
    Set2,
    Set3
}
public static readonly Dictionary<SetType, Constants> ConstantSets =
    new Dictionary<SetType, Constants> {
        [SetType.Set1] = new Constants { Name = "Set 1", Health = 100, MaxTries = null },
        [SetType.Set2] = new Constants { Name = "Set 2", Health = 80, MaxTries = 5 },
        ...
    };
public void Create(SetType set)
{
    var constants = ConstantSets[set];
    var foo = new Foo(constants.Name) {
        Health = constants.Health
    };
    if (constants.MaxTries is int maxTries) {
        foo.Add(maxTries);
    }
}