Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use generics with classes full of constants?

Tags:

c#

generics

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.

like image 566
Rakete1111 Avatar asked Dec 29 '25 22:12

Rakete1111


2 Answers

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);
}
like image 188
Fabjan Avatar answered Dec 31 '25 12:12

Fabjan


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>();
like image 42
Dmitry Bychenko Avatar answered Dec 31 '25 11:12

Dmitry Bychenko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!