[英]C# Base class with static list to be different between instantiated types?
我需要為我的對象創建唯一的ID,該ID可以在應用程序實例之間保存和加載。
我已經在當前的實現中工作了,但是這意味着我的每個類都需要幾乎相同的代碼,因此我決定創建一個包含該代碼的基類,然后從該基類繼承。 代碼如下。 我唯一的問題是,因為我在基類中有一個靜態列表 ,所以所有繼承的類類型都將添加到同一列表中。
因此,如何更改代碼,以使列表“項目”成為類型之間的不同列表?
澄清。 如果我有兩個類,則列出以下內容:Foo:UniqueObject Bar:UniqueObject我想讓Foo和Bar具有自己的靜態項目列表
abstract class UniqueObject
{
static protected List<UniqueObject> items = new List<UniqueObject>();
static Random rnd = new Random();
int id;
public int Object_ID { get { return id; } }
protected UniqueObject()
{
id = generateUniqueID();
items.Add(this);
}
protected UniqueObject(int Object_ID)
{
// Ensure the passed ID is unique
if (!isIdUnique(Object_ID))
{
// if not it does not get added to the items list and an exception is thrown.
throw new ArgumentNullException("Object ID is not unique. Already being used by another object.");
}
// If we get here then must have been unique so add it.
items.Add(this);
}
/// <summary>
/// Generates the unique identifier.
/// </summary>
/// <returns>The unique ID</returns>
private int generateUniqueID()
{
// get a random number
int val = rnd.Next();
// check it is unique, if not get another and try again.
while (!isIdUnique(val))
{
val = rnd.Next();
}
return val;
}
/// <summary>
/// Checks that the passed ID is unique against the other
/// objects in the 'items' list.
/// </summary>
/// <param name="ID">The identifier.</param>
/// <returns></returns>
private bool isIdUnique(int ID)
{
foreach (UniqueObject c in items)
{
if (c.id == ID)
return false;
}
return true;
}
}
我相信我可以使用泛型來實現這一點,因此我可以將類和列表更改為以下形式:
abstract class UniqueObject<T>
{
static protected List<T> items = new List<T>();
但這會給訂單項添加其他錯誤。
任何幫助將被申請。
如果您想要具有以下屬性的唯一ID:
1)在當前應用程序域中是唯一的
2)即使在處理應用程序的多個實例時,值也是唯一的。
然后,您需要考慮以下解決方案之一:
1)生成GUIDS
2)為您生成的ID提供一個唯一的“服務器”(可以提供您ID的通用服務器)
3)如果您確切知道您有多少個應用程序實例,則可以為每個實例定義一個“系列”的唯一ID。
最后,您需要將統一性的概念抽象為一個單獨的服務,您可以在應用程序的任何層/層中四處移動。 您的對象不得包含有關唯一性的邏輯,此概念是一個單獨的問題,您必須在其他組件中進行處理。 請應用關注點分離模式。
這就是我的實現(如果我願意的話)
public interface IEntityWithUniqueId
{
void SetUniqueId(string uniqueId);
string UniqueId { get; }
}
public interface IUniqueIdsProvider
{
string GetNewId();
}
public class UniqueObject : IEntityWithUniqueId
{
public string UniqueId { get; private set; }
void IEntityWithUniqueId.SetUniqueId(string uniqueId)
{
UniqueId = uniqueId;
}
}
public class MyObjects : UniqueObject
{
}
public class RemoteUniqueIdsProvider : IUniqueIdsProvider
{
public string GetNewId()
{
// calling a service ...., grab an unique ID
return Guid.NewGuid().ToString().Replace ("-", "");
}
}
public class UniqueObjectsFactory<T> where T : IEntityWithUniqueId, new ()
{
private IUniqueIdsProvider _uniqueIdsProvider;
public UniqueObjectsFactory(IUniqueIdsProvider uniqueIdsProvider)
{
_uniqueIdsProvider = uniqueIdsProvider;
}
public T GetNewEntity()
{
var x = new T();
x.SetUniqueId(_uniqueIdsProvider.GetNewId ());
return x;
}
}
我寫了這樣的測試方法:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void UniqueObjectTest()
{
var provider = new RemoteUniqueIdsProvider();
var factory = new UniqueObjectsFactory<MyObjects>(provider);
var entity = factory.GetNewEntity();
var entity2 = factory.GetNewEntity();
Assert.AreNotEqual(entity.UniqueId, entity2.UniqueId);
}
}
要解釋以上內容,請執行以下操作:1)接口IEntityWithUniqueId定義了“唯一”對象在應用程序中的外觀,因此它是一個具有UniqueId屬性和特殊方法的對象:SetUniqueId。 我沒有使用get和set設置屬性UniqueId,因為“ set”將是基礎結構操作,而get將是開發人員API。
2)接口IUniqueIdsProvider告訴您唯一ID提供程序的外觀。 它必須有一個簡單的方法:GetNewId(); 為您提供唯一的ID。 實施可以在任何地方(在服務器上,在本地等)
3)UniqueObject類。 此類是所有唯一對象的基類。
4)UniqueObjectsFactory。 這是為您提供新的獨特對象的類。 從磁盤加載對象時,必須從生成唯一ID的假設開始,因此在加載它們時,不必再次檢查唯一性。
關於使用泛型的最后一句話,我想您可以這樣做:
abstract class UniqueObject<T> where T : class
接着
items.Add(this as T);
這應該可以工作,並且如果您不顯式使用UniqueObject<>
那么this as T
應該永遠不會在運行時失敗。
我不確定我對泛型類型擁有靜態成員的感覺( 您不應該這樣做 ),但這至少應該有效
更新 :是的, 似乎可行
在我的回答中,我試圖准確回答您的要求。 綜上所述,如果您需要的只是對象的唯一ID,並檢查在創建對象時是否沒有重復的ID,則可以:
使用GUID,而不必進行檢查。 GUID沖突在理論上是可能的...。但是會發生嗎? 在正常情況下,更有可能不會。 即使您一年內創建了上萬億的GUID,也有很大的機會您的程序會因隕石撞擊計算機多次而崩潰,然后再找到一個重復的GUID。
不過,如果您要檢查並絕對確定(實際上這是一件公平的事情),這可能會更容易,並且您不需要按類型存儲整個對象的列表即可執行此操作...參見這個簡單的基類,它將以與您執行操作類似的方式執行您想要的操作:
abstract class UniqueObject : IDisposable { static protected HashSet<Guid> Guids = new HashSet<Guid>(); Guid _id; public Guid ObjectID { get { return _id; } } protected UniqueObject() { do { _id = Guid.NewGuid(); } while(Guids.Contains(_id)); Guids.Add(_id); } protected UniqueObject(Guid guid) { if(Guids.Contains(guid)) throw new ArgumentNullException("Object ID is not unique. Already being used by another object."); _id = guid; } // Make a better implementation of IDisposable public void Dispose() { guids.Remove(_id); } }
就是這樣。 如果仍然要使用int
而不是Guid
,則可以將其更改為int
,並具有以下內容:
// static members
static protected Random rnd = new Random();
static protected HashSet<int> ids = new HashSet<int>();
// ... in the constructor:
do
{
_id = rnd.Next();
} while(ids.Contains(_id));
看起來和以前一樣
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.