Reputation: 49018
I hope it becomes clear what I mean. I have multiple static class full of options:
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.
}
Now I want to use those options to create a class which includes the contents of one of these classes.
public void Create<T>()
{
var foo = new Foo(T.Name);
foo.Prop = T.Something;
if (T.HasValue)
foo.Add(T.Value);
}
But of course this doesn't work. I would use interfaces, but static classes can't implement interfaces.
Is there any way to make this work elegantly? Making Thing1
and Thing2
singletons would work, but that isn't a very nice solution.
I could create a struct and put the objects into another static class, but I was wondering whether you could do something like the above.
Upvotes: 2
Views: 775
Reputation: 13676
Well, you can create an interface and make your classes non-static and inherit from this interface:
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; }
}
And then use it for your method together with type parameter constraint:
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);
}
Upvotes: 3
Reputation: 112632
After some reflection I came up with another solution. Why select the constants by type? It is much easier if we use the same type to store the different sets of constants.
public class Constants
{
public string Name { get; set; }
public double Health { get; set; }
public int? MaxTries { get; set; }
}
We then identify the sets through an enum
:
public enum SetType
{
Set1, // Please use speaking names in a real implementation!
Set2,
Set3
}
We define the values of the constants while creating the dictionary of constants sets:
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 },
...
};
The Create
method becomes
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);
}
}
No generics, no reflection, no fancy stuff required.
Upvotes: 1
Reputation: 186813
You can try Reflection: scan assemblies for static
classes, obtain public const string
fields with their values from them and materialize them as a Dictionary<T>
using System.Linq;
using System.Reflection;
...
// Key : type + field name, say Tuple.Create(typeof(Thing1), "Name")
// Value : corresponding value, say "Thing 1";
static Dictionary<Tuple<Type, string>, string> s_Dictionary = AppDomain
.CurrentDomain
.GetAssemblies() // I've taken all assemblies; you may want to add Where here
.SelectMany(asm => asm.GetTypes())
.Where(t => t.IsAbstract && t.IsSealed) // All static types, you may want to add Where
.SelectMany(t => t
.GetFields() // All constant string fields
.Where(f => f.FieldType == typeof(string))
.Where(f => f.IsPublic && f.IsStatic)
.Where(f => f.IsLiteral && !f.IsInitOnly) // constants only
.Select(f => new {
key = Tuple.Create(t, f.Name),
value = f.GetValue(null)
}))
.ToDictionary(item => item.key, item => item.value?.ToString());
If you want to scan not all loaded but just one (executing) assembly
static Dictionary<Tuple<Type, string>, string> s_Dictionary = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsAbstract && t.IsSealed)
...
Now you can wrap the dictionary, say
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
}
Usage
string name1 = ReadConstant<Thing1>();
Upvotes: 2
Reputation: 32296
Instead of a static classes with constants. You can create a class with properties and static instances with the desired values.
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);
}
And then your method would just take that class.
public void Create(Thing t)
{
var foo = new Foo(t.Name);
foo.Prop = t.Something;
if (t.HasValue)
foo.Add(t.Value);
}
Then you'd call it with either
Create(Thing.Thing1);
or
Create(Thing.Thing2);
Upvotes: 1
Reputation: 112632
You could use non static classes and add those to a dictionary having a Type
key. But you would have to use read-only properties.
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;
}
Initialize the dictionary:
public static readonly Dictionary<Type, IConstants> Constants =
new Dictionary<Type, IConstants> {
[typeof(Thing1)] = new Thing1(),
[typeof(Thing2)] = new Thing2(),
};
The Create
function:
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);
}
}
Upvotes: 1