簡體   English   中英

具有靜態列表的C#基類在實例化類型之間是否有所不同?

[英]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,則可以:

  1. 使用GUID,而不必進行檢查。 GUID沖突在理論上是可能的...。但是會發生嗎? 在正常情況下,更有可能不會。 即使您一年內創建了上萬億的GUID,也有很大的機會您的程序會因隕石撞擊計算機多次而崩潰,然后再找到一個重復的GUID。

  2. 不過,如果您要檢查並絕對確定(實際上這是一件公平的事情),這可能會更容易,並且您不需要按類型存儲整個對象的列表即可執行此操作...參見這個簡單的基類,它將以與您執行操作類似的方式執行您想要的操作:

     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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM