C# 具有泛型C的访问常量#

C# 具有泛型C的访问常量#,c#,generics,inheritance,C#,Generics,Inheritance,我有以下基类: public class Base { public string LogicalName { get; set; } public int NumberOfChars { get; set; } public Base() { } public Base(string logicalName, int numberOfChars) { LogicalName = logicalName;

我有以下基类:

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;
    }  
}
以及以下派生类:

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)
    {

    }
}
我还有一个服务提供的功能:

public IEnumerable<T> GetEntities<T>(string entityName, int numberOfChars) where T : Base
{
    //Some code to get the entities
}
public IEnumerable GetEntities(字符串entityName,int numberOfChars),其中T:Base
{
//一些获取实体的代码
}
我的问题是如何一般地调用这个函数?我想用这样的东西来称呼它:

public void TestEntities<T>() where T : Base
{
    var entities = GetEntities<T>(T.EntityLogicalName, T.EntityNumberOfChars);

    //some other code to test the entities
}
public void TestEntities(),其中T:Base
{
var entities=GetEntities(T.EntityLogicalName,T.EntityNumberOfChars);
//测试实体的其他一些代码
}

这当然不起作用,因为在这一点上,t是未知的。我怎样才能完成类似的事情?EntityLogicalName和EntityNumberOfChars是所有基类派生类都具有的特征,它们对于每个派生类都不会改变。我能在不实例化对象或其他我看不到的方式的情况下从基类获取它们吗?

用getter抽象属性替换常量

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()
   {

    }
}
此外,您还可以将一些逻辑放入重写的getter中,例如:

...
public string LogicalName { get { return this.EntityMap.Name; } } 
...
更新:您不希望从类实例化对象,但希望能够以强类型方式获取该字符串,这一事实可以通过另一种方式处理。它与上面的答案完全不同(因为在c#中不能覆盖静态道具)。考虑下面的代码。我们在这里又添加了一个类,但LocatorInner可以是基类的成员。我们在一些现有应用程序中大量使用这种方法:

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>();
    }
}
公共类定位器
{
公共静态类定位器,其中T:BaseClass
{
公共静态字符串名称{get;set;}
}
公共静态字符串GetName(),其中T:BaseClass
{
返回locator.Name;
}
公共静态void SetName(字符串名称),其中T:BaseClass
{
LocatorInner.Name=名称;
}
}
公共类基类
{
}
公共类派生类:基类
{
静态派生类()
{
Locator.LocatorInner.Name=“我”;
}
}
公共类TestClass,其中T:BaseClass
{
公开作废法()
{
var name=Locator.GetName();
}
}

如@vittore所述,将属性移动到base,从派生和创建中传递硬编码值,使用defautl(T)

public IEnumerable GetEntities(字符串entityName,int numberOfChars),其中T:Base
{
yield return default(T);//是它的always类use new constraint并返回new T();
}

IMHO,我认为在这里使用常量是一个糟糕的设计决策

您可以使用@vittore方法解决这个问题,但对我来说,如果您希望从
T
泛型参数中获取数据,那么您应该使用带有属性的元编程

例如,关于:

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()
   {

    }
}
现在,您的服务方法可以提取属性元数据,如下所示:

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
}
public void TestEntities(),其中T:Base
{
LogicalNameAttribute logicalNameAttr=typeof(T).GetCustomAttribute();
NumberOfCharsAttribute numberOfCharsAttr=typeof(T).GetCustomAttribute();
Assert(logicalNameAttr!=null);
Assert(numberOfCharsAttr!=null);
字符串logicalName=logicalNameAttr.Name;
int numberOfChars=numberOfCharsAttr.Number;
//其他东西
}

由于需要使用反射来获取应用于
T
的属性,因此存在性能损失,但是您可以灵活地不强制派生类提供此静态信息。

为什么不能将这些EntityLogicalName和EntityNumberOfChars移动到基类中呢..因为它们对于每个派生类都是不同的,它们的用途不是用作实例属性,而是用作类型常量(或静态)。方法TestEntities只知道要获取哪种实体类型,但我需要将这些常量传递给该方法;是吗?不是,因为T是一种类型,而不是一个实例,所以
Derived1
中缺少类似于
override
关键字的ooks,但是那样的话,为了将这些属性传递给GetEntities,我将实例化一个Derived1对象。EntityLogicalName和EntityNumberOfChars是常量。就像说一辆车是一辆车,有4个轮子,而不需要构造一辆车来知道这一点。另一方面,摩托车也是一种车辆,其车轮数为2。所有车辆都应该有一个表示车轮数量的静态构件,但我不知道如何表示。此外,我还需要能够从继承自vehicle的泛型类型中获取这些成员。我不确定这个示例有什么问题。您将有两个派生类,一个用于汽车,一个用于自行车,一个具有
public int NumberOfWeels{get{return 4;}}
,而另一个
public int NumberOfWheels{get{return 2;}}
。但是,如果您需要在类级别而不是实例级别上工作,也就是说,您不想构造类的实例,只需将这些属性设置为静态。@vittore这就是我所做的,但我将它们设置为常量而不是静态。但我的问题是相同的,在测试中,如何从T获得“NumberOfWheels”?作为旁注,TestEntities将被动态调用到派生类列表中,这意味着我不能将'NumberOfWheels'作为参数传递给TestEntities(我应该以一辆汽车作为示例,而不是基本和派生:)。在TestEntities中,使用我的代码,您将拥有
(new t()).NumberOfChars
。唯一需要约束的东西是:constraint
public IEnumerable GetEntities(string entityName,int numberOfChars),其中T:Base,new()
GetEntities来自一个我无权访问的服务。在某个地方,你会给T赋值。从那个地方传递实体名。public void TestEntities(字符串entityLogicalName,int entityNumbero
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
}