Access const with C # generics

I have the following base class:

public class Base { public string LogicalName { get; set; } public int NumberOfChars { get; set; } public Base() { } public Base(string logicalName, int numberOfChars) { LogicalName = logicalName; NumberOfChars = numberOfChars; } } 

and the following derived classes:

 public class Derived1 : Base { public const string EntityLogicalName = "Name1"; public const int EntityNumberOfChars = 30; public Derived1() : base(EntityLogicalName, EntityNumberOfChars) { } } public class Derived2 : Base { public const string EntityLogicalName = "Name2"; public const int EntityNumberOfChars = 50; public Derived2() : base(EntityLogicalName, EntityNumberOfChars) { } } 

and I also have this function provided by the service:

 public IEnumerable<T> GetEntities<T>(string entityName, int numberOfChars) where T : Base { //Some code to get the entities } 

My problem is how can I call this function in general? I want to call it something that looks like this:

 public void TestEntities<T>() where T : Base { var entities = GetEntities<T>(T.EntityLogicalName, T.EntityNumberOfChars); //some other code to test the entities } 

This, of course, does not work, because at the moment T is unknown. How can I do something like this? EntityLogicalName and EntityNumberOfChars are characteristics that all Base classes have, and they never change for each derived class. Can I get them from the base class without instantiating objects or in any other way that I don't see?

+4
source share
3 answers

IMHO, I believe that using constants here is a poor design decision.

You can solve the problem with the @vittore approach, but for me it seems like you should use metaprogramming with attributes if you want to get data from T common argument

For example, what about:

 public class LogicalNameAttribute : Attribute { public LogicalNameAttribute(string name) { Name = name; } public string Name { get; private set; } } public class NumberOfCharsAttribute : Attribute { public NumberOfCharsAttribute (int number) { Number = number; } public string Number { get; private set; } } [LogicalName("Name1"), NumberOfChars(30)] public class Derived1 : Base { public Derived1() : base() { } } 

Now your service method can retrieve attribute metadata as follows:

 public void TestEntities<T>() where T : Base { LogicalNameAttribute logicalNameAttr = typeof(T).GetCustomAttribute<LogicalNameAttribute>(); NumberOfCharsAttribute numberOfCharsAttr = typeof(T).GetCustomAttribute<NumberOfCharsAttribute >(); Contract.Assert(logicalNameAttr != null); Contract.Assert(numberOfCharsAttr != null); string logicalName = logicalNameAttr.Name; int numberOfChars = numberOfCharsAttr.Number; // Other stuff } 

There is a performance score there because you need to use reflection to get the attributes applied to T , but you get the flexibility of not having the derived classes provide this static information.

+2
source

Replace constants with abstract getter properties

 public abstract class Base { public abstract string LogicalName { get; } public abstract int NumberOfChars { get; } public Base() { } } public class Derived1 : Base { public string LogicalName { get { return "Name1"; } } public int NumberOfChars { get { return 30; } } public Derived1() : base() { } } 

In addition, you can introduce some logic into a redundant getter, for example.

 ... public string LogicalName { get { return this.EntityMap.Name; } } ... 

UPDATE. The fact that you do not want to instantiate an object from a class, but want to be able to get this string in a strongly typed way, can be handled in yet another way. It is completely separate from the answer above (since you cannot override static details in C #). Consider the following code. Here we add another class, but LocatorInner can be a member of BaseClass. We use this approach a lot in several existing applications:

 public class Locator { public static class LocatorInner<T> where T : BaseClass { public static string Name { get; set; } } public static string GetName<T>() where T : BaseClass { return LocatorInner<T>.Name; } public static void SetName<T>(string name) where T : BaseClass { LocatorInner<T>.Name = name; } } public class BaseClass { } public class DerivedClass: BaseClass { static DerivedClass() { Locator.LocatorInner<DerivedClass>.Name = "me"; } } public class TestClass<T> where T : BaseClass { public void Method() { var name = Locator.GetName<T>(); } } 
+5
source

As @vittore mentioned, move the properties to the base, pass the hard-coded values ​​from the derivatives, and use only defautl (T) in the creation

 public IEnumerable<T> GetEntities<T>(string entityName, int numberOfChars) where T : Base { yield return default(T); //Is its always class use new constraint and return new T(); } 
0
source

All Articles