在 C# 中使用静态 属性 继承抽象 class
inheritance of abstract class with static property in C#
短版:
我有一个抽象 class A。它有一个方法需要知道每个子 class 特定的静态 class 属性 的值.名称和类型相同,只是每个子的值可以是唯一的class.
我可以在基础 class A 中定义这个静态 属性 以便能够使用 A 中定义的方法访问它,但保持 属性 的不同子值class不相关?
或者我将如何实现类似的东西?
长版:
假设我有一个数据模型的抽象基础class。它有一个 public 属性 Id
(Int32)。
我想在基础 class 中实现一个构造函数,它根据子对象的最后分配 ID 生成一个新 ID class。
原因是真正的ID是由数据库自动分配的,但是每个数据模型对象在构建时已经有一个唯一的ID而不是写入数据库还。由于数据库只分配正整数作为 ID,我的计划是为新创建的数据模型对象分配一个临时的、唯一的负 ID。写入对象后,ID 将更改为真实 ID。
因为我有很多不同的数据模型 class 都是从我的抽象基础 class 派生的,我认为最好在其中包含该功能而不是重复它。但是每个 subclass 必须有自己的计数器指向下一个空闲的负 ID,因为不同的 classes' ID 是不相关的。
所以我需要在每个 subclass 中有一个静态 属性 来存储这个 class' 最后分配的临时 ID,但是分配它的机制总是相同的并且可以实现进入抽象基础 class' 构造函数。但是,我无法从必须由子 class 实现的基础 class 访问 属性,这意味着我必须在基础 class 中定义它].但是这个静态 属性 会不会对所有子 class 都是全局的,这不是我想要的?
如何以最优雅的方式实现这个临时 ID 计数器?
简化代码示例:
public abstract class ModelBase
{
public Int32 Id { get; set; }
protected static Int32 LastTempId { get; set; } = 0;
public ModelBase()
{
Id = --LastTempId;
}
}
public class Model1 : ModelBase
{
public Model1 () : base ()
{
// do something model1-specific
}
}
public class Model2 : ModelBase
{
public Model2() : base()
{
// do something model2-specific
}
}
如果我这样实现,我担心对于 subclasses model1
和 model2
,继承的静态 属性 LastTempId
将是同一个实例。但是我想为每个 subclass 一个单独的计数器,同时仍然在基础 class 构造函数中使用它。
好吧,静态 classes 不是继承的,所以这已经过时了,你不能强制子classes 实现静态方法,所以这也是过时了。
与其将该方法放在 class 本身中,不如拥有一个您可以实现的基本接口。 那么你可以有一个可以抽象的实例方法:
public interface IDataModelFactory<T> where T:ModelBase
{
int GetLastTempId();
}
public Model1Factory : IDataModelFactory<Model1>
{
public int GetLastTempId()
{
// logic for Model1
}
}
public Model2Factory : IDataModelFactory<Model2>
{
public int GetLastTempId()
{
// logic for Model2
}
}
或者如果逻辑对所有 classes 都是通用的,则有一个带有(或不带有)接口的抽象基础 class:
public DataModelFactory<T> : IDataModelFactory<T>
{
public virtual int GetLastTempId()
{
// common logic
}
// other common logic
}
您甚至可以将工厂设为单例,这样您就不必一直创建实例,它们甚至可以是模型 class 的子 class,因此它们'重新紧密相连。
附带说明一下,如果您不确定 inheritance/interface 关系是什么,我经常发现从 copy/paste 重用和重构代码以引入基础 classes 和接口。这样您 知道 通用代码是什么,并且可以将其重构为通用方法。否则,您很想尝试将 所有内容 放入基础 class 中,并使用开关或其他构造来根据派生类型更改逻辑。
简答
子class不能为静态属性设置不同的值,因为静态属性是class的属性,不是它的实例,它不是继承的。
长答案
您可以在抽象 class 上将单个计数器实现为静态 属性,并让抽象 class 的一个构造函数使用它。
编辑:要为每个子 class 保存不同的计数器,您可以使用静态字典将类型 (sub-class) 映射到柜台.
public abstract class A<T>
{
public static Dictionary<Type, int> TempIDs = new Dictionary<Type, int>();
public int ID { get; set; }
public A()
{
if (!TempIDs.ContainsKey(typeof(T)))
TempIDs.Add(typeof(T), 0);
this.ID = TempIDs[typeof(T)] - 1;
TempIDs[typeof(T)]--;
}
}
public class B : A<B>
{
public string Foo { get; set; }
public B(string foo)
: base()
{
this.Foo = foo;
}
}
public class C : A<C>
{
public string Bar { get; set; }
public C(string bar)
: base()
{
this.Bar = bar;
}
}
B b1 = new B("foo");
B b2 = new B("bar");
C c1 = new C("foo");
C c2 = new C("foo");
b1.ID
会是 -1
,b2.ID
会是 -2
,c1.ID
会是 -1
,c2.ID
会成为 -2
首先,我的拙见是实体不应该负责分配他们自己的唯一标识符。保持明确的关注点分离。
该游戏中应该有另一个玩家应该分配这些临时唯一标识符(无论是负整数还是正整数)。
通常,所谓的其他播放器是 repository design pattern 的实现,它负责将域(您的模型)转换为数据的最终表示,反之亦然。
通常 repository 有一个方法可以 添加对象 。这应该是您设置这些临时标识符的地方:
public void Add(Some some)
{
some.Id = [call method here to set the whole id];
}
而且,大多数存储库实施 每个实体 。
- 客户资料库
- 发票存储库
- ...
...但这并不妨碍您定义一个基本存储库 class,它可以实现处理某些实体类型时的共同点:
public interface IRepository<TEntity> where TEntity : EntityBase
{
// Other repository methods should be defined here
// but I just define Add for the convenience of this
// Q&A
void Add(TEntity entity);
}
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : EntityBase
{
public virtual void Add(TEntity entity)
{
entity.Id = [call method here to set the whole id];
}
}
...现在任何 class 派生 Repository<TEntity>
将能够为其专门实体生成一个临时标识符:
public class CustomerRepository : Repository<Customer> { }
public class InvoiceRepository : Repository<Invoice> { }
如何将唯一的临时实体标识符作为抽象存储库的一部分实施 class 并能够针对每个特定实体类型执行此操作?
使用字典存储每个实体最后分配的标识符实现 属性 到 Repository<TEntity>
:
public Dictionary<Type, int> EntityIdentifiers { get; } = new Dictionary<Type, int>();
...以及减少下一个临时标识符的方法:
private static readonly object _syncLock = new object();
protected virtual void GetNextId()
{
int nextId;
// With thread-safety to avoid unwanted scenarios.
lock(_syncLock)
{
// Try to get last entity type id. Maybe the id doesn't exist
// and out parameter will set default Int32 value (i.e. 0).
bool init = EntityIdentifiers.TryGetValue(typeof(TEntity), out nextId);
// Now decrease once nextId and set it to EntityIdentifiers
nextId--;
if(!init)
EntityIdentifiers[typeof(TEntity)] = nextId;
else
EntityIdentifiers.Add(typeof(TEntity), nextId);
}
return nextId;
}
最后,您的 Add
方法可能如下所示:
public virtual void Add(TEntity entity)
{
entity.Id = GetNextId();
}
一种方法是反射,但它需要 运行 时间并且容易出现 运行 时间错误。正如其他人提到的:您不能强制继承 classes 以 重新声明 某些静态字段并能够在祖先 class 中使用该字段。所以我认为最小的代码冗余是必要的:每个继承 class 应该提供它自己的密钥生成器。这个生成器当然可以保存在class的静态字段中。
(注意这不一定是线程安全的。)
class KeyGenerator
{
private int _value = 0;
public int NextId()
{
return --this._value;
}
}
abstract class ModelBase
{
private KeyGenerator _generator;
public ModelBase(KeyGenerator _generator)
{
this._generator = _generator;
}
public void SaveObject()
{
int id = this._generator.NextId();
Console.WriteLine("Saving " + id.ToString());
}
}
class Car : ModelBase
{
private static KeyGenerator carKeyGenerator = new KeyGenerator();
public Car()
: base(carKeyGenerator)
{
}
}
class Food : ModelBase
{
private static KeyGenerator foodKeyGenerator = new KeyGenerator();
public Food()
: base(foodKeyGenerator)
{
}
}
class Program
{
static void Main(string[] args)
{
Food food1 = new Food();
Food food2 = new Food();
Car car1 = new Car();
food1.SaveObject();
food2.SaveObject();
car1.SaveObject();
}
}
这会产生:
Saving -1
Saving -2
Saving -1
只需在每个对象添加到您的数据库之前为其生成一个 GUID。您可以有一个 isAdded 标志,告诉您应该将对象称为 GUID,或者在添加对象后清除 GUID。使用 GUID,您永远不必担心两个对象会发生冲突。它还避免了每个子类需要单独的 ID。我不会像你建议的那样为两个州重复使用相同的 属性。
https://msdn.microsoft.com/en-us/library/system.guid(v=vs.110).aspx
短版:
我有一个抽象 class A。它有一个方法需要知道每个子 class 特定的静态 class 属性 的值.名称和类型相同,只是每个子的值可以是唯一的class.
我可以在基础 class A 中定义这个静态 属性 以便能够使用 A 中定义的方法访问它,但保持 属性 的不同子值class不相关?
或者我将如何实现类似的东西?
长版:
假设我有一个数据模型的抽象基础class。它有一个 public 属性 Id
(Int32)。
我想在基础 class 中实现一个构造函数,它根据子对象的最后分配 ID 生成一个新 ID class。
原因是真正的ID是由数据库自动分配的,但是每个数据模型对象在构建时已经有一个唯一的ID而不是写入数据库还。由于数据库只分配正整数作为 ID,我的计划是为新创建的数据模型对象分配一个临时的、唯一的负 ID。写入对象后,ID 将更改为真实 ID。
因为我有很多不同的数据模型 class 都是从我的抽象基础 class 派生的,我认为最好在其中包含该功能而不是重复它。但是每个 subclass 必须有自己的计数器指向下一个空闲的负 ID,因为不同的 classes' ID 是不相关的。
所以我需要在每个 subclass 中有一个静态 属性 来存储这个 class' 最后分配的临时 ID,但是分配它的机制总是相同的并且可以实现进入抽象基础 class' 构造函数。但是,我无法从必须由子 class 实现的基础 class 访问 属性,这意味着我必须在基础 class 中定义它].但是这个静态 属性 会不会对所有子 class 都是全局的,这不是我想要的?
如何以最优雅的方式实现这个临时 ID 计数器?
简化代码示例:
public abstract class ModelBase
{
public Int32 Id { get; set; }
protected static Int32 LastTempId { get; set; } = 0;
public ModelBase()
{
Id = --LastTempId;
}
}
public class Model1 : ModelBase
{
public Model1 () : base ()
{
// do something model1-specific
}
}
public class Model2 : ModelBase
{
public Model2() : base()
{
// do something model2-specific
}
}
如果我这样实现,我担心对于 subclasses model1
和 model2
,继承的静态 属性 LastTempId
将是同一个实例。但是我想为每个 subclass 一个单独的计数器,同时仍然在基础 class 构造函数中使用它。
好吧,静态 classes 不是继承的,所以这已经过时了,你不能强制子classes 实现静态方法,所以这也是过时了。
与其将该方法放在 class 本身中,不如拥有一个您可以实现的基本接口。 那么你可以有一个可以抽象的实例方法:
public interface IDataModelFactory<T> where T:ModelBase
{
int GetLastTempId();
}
public Model1Factory : IDataModelFactory<Model1>
{
public int GetLastTempId()
{
// logic for Model1
}
}
public Model2Factory : IDataModelFactory<Model2>
{
public int GetLastTempId()
{
// logic for Model2
}
}
或者如果逻辑对所有 classes 都是通用的,则有一个带有(或不带有)接口的抽象基础 class:
public DataModelFactory<T> : IDataModelFactory<T>
{
public virtual int GetLastTempId()
{
// common logic
}
// other common logic
}
您甚至可以将工厂设为单例,这样您就不必一直创建实例,它们甚至可以是模型 class 的子 class,因此它们'重新紧密相连。
附带说明一下,如果您不确定 inheritance/interface 关系是什么,我经常发现从 copy/paste 重用和重构代码以引入基础 classes 和接口。这样您 知道 通用代码是什么,并且可以将其重构为通用方法。否则,您很想尝试将 所有内容 放入基础 class 中,并使用开关或其他构造来根据派生类型更改逻辑。
简答
子class不能为静态属性设置不同的值,因为静态属性是class的属性,不是它的实例,它不是继承的。
长答案
您可以在抽象 class 上将单个计数器实现为静态 属性,并让抽象 class 的一个构造函数使用它。
编辑:要为每个子 class 保存不同的计数器,您可以使用静态字典将类型 (sub-class) 映射到柜台.
public abstract class A<T>
{
public static Dictionary<Type, int> TempIDs = new Dictionary<Type, int>();
public int ID { get; set; }
public A()
{
if (!TempIDs.ContainsKey(typeof(T)))
TempIDs.Add(typeof(T), 0);
this.ID = TempIDs[typeof(T)] - 1;
TempIDs[typeof(T)]--;
}
}
public class B : A<B>
{
public string Foo { get; set; }
public B(string foo)
: base()
{
this.Foo = foo;
}
}
public class C : A<C>
{
public string Bar { get; set; }
public C(string bar)
: base()
{
this.Bar = bar;
}
}
B b1 = new B("foo");
B b2 = new B("bar");
C c1 = new C("foo");
C c2 = new C("foo");
b1.ID
会是 -1
,b2.ID
会是 -2
,c1.ID
会是 -1
,c2.ID
会成为 -2
首先,我的拙见是实体不应该负责分配他们自己的唯一标识符。保持明确的关注点分离。
该游戏中应该有另一个玩家应该分配这些临时唯一标识符(无论是负整数还是正整数)。
通常,所谓的其他播放器是 repository design pattern 的实现,它负责将域(您的模型)转换为数据的最终表示,反之亦然。
通常 repository 有一个方法可以 添加对象 。这应该是您设置这些临时标识符的地方:
public void Add(Some some)
{
some.Id = [call method here to set the whole id];
}
而且,大多数存储库实施 每个实体 。
- 客户资料库
- 发票存储库
- ...
...但这并不妨碍您定义一个基本存储库 class,它可以实现处理某些实体类型时的共同点:
public interface IRepository<TEntity> where TEntity : EntityBase
{
// Other repository methods should be defined here
// but I just define Add for the convenience of this
// Q&A
void Add(TEntity entity);
}
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : EntityBase
{
public virtual void Add(TEntity entity)
{
entity.Id = [call method here to set the whole id];
}
}
...现在任何 class 派生 Repository<TEntity>
将能够为其专门实体生成一个临时标识符:
public class CustomerRepository : Repository<Customer> { }
public class InvoiceRepository : Repository<Invoice> { }
如何将唯一的临时实体标识符作为抽象存储库的一部分实施 class 并能够针对每个特定实体类型执行此操作?
使用字典存储每个实体最后分配的标识符实现 属性 到 Repository<TEntity>
:
public Dictionary<Type, int> EntityIdentifiers { get; } = new Dictionary<Type, int>();
...以及减少下一个临时标识符的方法:
private static readonly object _syncLock = new object();
protected virtual void GetNextId()
{
int nextId;
// With thread-safety to avoid unwanted scenarios.
lock(_syncLock)
{
// Try to get last entity type id. Maybe the id doesn't exist
// and out parameter will set default Int32 value (i.e. 0).
bool init = EntityIdentifiers.TryGetValue(typeof(TEntity), out nextId);
// Now decrease once nextId and set it to EntityIdentifiers
nextId--;
if(!init)
EntityIdentifiers[typeof(TEntity)] = nextId;
else
EntityIdentifiers.Add(typeof(TEntity), nextId);
}
return nextId;
}
最后,您的 Add
方法可能如下所示:
public virtual void Add(TEntity entity)
{
entity.Id = GetNextId();
}
一种方法是反射,但它需要 运行 时间并且容易出现 运行 时间错误。正如其他人提到的:您不能强制继承 classes 以 重新声明 某些静态字段并能够在祖先 class 中使用该字段。所以我认为最小的代码冗余是必要的:每个继承 class 应该提供它自己的密钥生成器。这个生成器当然可以保存在class的静态字段中。
(注意这不一定是线程安全的。)
class KeyGenerator
{
private int _value = 0;
public int NextId()
{
return --this._value;
}
}
abstract class ModelBase
{
private KeyGenerator _generator;
public ModelBase(KeyGenerator _generator)
{
this._generator = _generator;
}
public void SaveObject()
{
int id = this._generator.NextId();
Console.WriteLine("Saving " + id.ToString());
}
}
class Car : ModelBase
{
private static KeyGenerator carKeyGenerator = new KeyGenerator();
public Car()
: base(carKeyGenerator)
{
}
}
class Food : ModelBase
{
private static KeyGenerator foodKeyGenerator = new KeyGenerator();
public Food()
: base(foodKeyGenerator)
{
}
}
class Program
{
static void Main(string[] args)
{
Food food1 = new Food();
Food food2 = new Food();
Car car1 = new Car();
food1.SaveObject();
food2.SaveObject();
car1.SaveObject();
}
}
这会产生:
Saving -1
Saving -2
Saving -1
只需在每个对象添加到您的数据库之前为其生成一个 GUID。您可以有一个 isAdded 标志,告诉您应该将对象称为 GUID,或者在添加对象后清除 GUID。使用 GUID,您永远不必担心两个对象会发生冲突。它还避免了每个子类需要单独的 ID。我不会像你建议的那样为两个州重复使用相同的 属性。
https://msdn.microsoft.com/en-us/library/system.guid(v=vs.110).aspx