简体   繁体   English

具有静态列表的C#基类在实例化类型之间是否有所不同?

[英]C# Base class with static list to be different between instantiated types?

I have a need to create unique ID's for my objects which can be saved and loaded between application instances. 我需要为我的对象创建唯一的ID,该ID可以在应用程序实例之间保存和加载。

I have this working in my current implementation but it means each of my classes needs almost the same piece of code, therefore I decided to create a base class with this code in and then inherit from that. 我已经在当前的实现中工作了,但是这意味着我的每个类都需要几乎相同的代码,因此我决定创建一个包含该代码的基类,然后从该基类继承。 The code is below. 代码如下。 The only issue I'm having is that because I have a static list in the base class, all inherited class types are getting added to the same list. 我唯一的问题是,因为我在基类中有一个静态列表 ,所以所有继承的类类型都将添加到同一列表中。

Therefore how can I change the code so that the List 'items' is a different list between types? 因此,如何更改代码,以使列表“项目”成为类型之间的不同列表?

To clarify. 澄清。 If I have two classes list this: Foo: UniqueObject Bar: UniqueObject I want to the Foo and Bar to have their own static item list 如果我有两个类,则列出以下内容: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;
    }
}

I believe I can achieve this using Generics so i could change the class and list to something like this: 我相信我可以使用泛型来实现这一点,因此我可以将类和列表更改为以下形式:

abstract class UniqueObject<T>
{
    static protected List<T> items = new List<T>();

But this gives other errors with the line items.Add(this). 但这会给订单项添加其他错误。

Any help would be appriciated. 任何帮助将被申请。

If you want an unique id that has the folowing properties: 如果您想要具有以下属性的唯一ID:

1) Is unique in current app domain 1)在当前应用程序域中是唯一的

2) Values are unique even when dealing with multiple instances of your application. 2)即使在处理应用程序的多个实例时,值也是唯一的。

Then you need to consider one of these solutions: 然后,您需要考虑以下解决方案之一:

1) Generate GUIDS 1)生成GUIDS

2) Have a unique "server" for your generated ids (a common server that can serve your ids) 2)为您生成的ID提供一个唯一的“服务器”(可以提供您ID的通用服务器)

3) If you know exactly how many application instances you have, you can define a "series" of unique ids for each instance. 3)如果您确切知道您有多少个应用程序实例,则可以为每个实例定义一个“系列”的唯一ID。

And last, you need to abstract the notion of unicity into a separate service that you can move around in whatever tier / layer of your application. 最后,您需要将统一性的概念抽象为一个单独的服务,您可以在应用程序的任何层/层中四处移动。 Your objects must NOT contain logic about unicity, this notion is a separate concern and you must be deal with it in other component. 您的对象不得包含有关唯一性的逻辑,此概念是一个单独的问题,您必须在其他组件中进行处理。 Please apply the separation of concerns pattern. 请应用关注点分离模式。

So this is my implementation (if I would be you) 这就是我的实现(如果我愿意的话)

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;
    }
}

I wrote a test method like this: 我写了这样的测试方法:

[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);


        }

    }

To explain what is above: 1) The interface IEntityWithUniqueId defines how an "unique" object must look like in your application, so it is an object that has an UniqueId property and also a special method: SetUniqueId. 要解释以上内容,请执行以下操作:1)接口IEntityWithUniqueId定义了“唯一”对象在应用程序中的外观,因此它是一个具有UniqueId属性和特殊方法的对象:SetUniqueId。 I didn't made the property UniqueId with get and set because "set" would be an infrastructure operation but get will be a developer API. 我没有使用get和set设置属性UniqueId,因为“ set”将是基础结构操作,而get将是开发人员API。

2) The interface IUniqueIdsProvider tells you how a unique ids provider will look like. 2)接口IUniqueIdsProvider告诉您唯一ID提供程序的外观。 It must have a simple method: GetNewId (); 它必须有一个简单的方法:GetNewId(); that serves you an unique Id. 为您提供唯一的ID。 The implementation can be anywhere (on a server, locally, etc) 实施可以在任何地方(在服务器上,在本地等)

3) UniqueObject class. 3)UniqueObject类。 This class is the base class for all your unique objects. 此类是所有唯一对象的基类。

4) UniqueObjectsFactory. 4)UniqueObjectsFactory。 This is the class that serves you new unique objects. 这是为您提供新的独特对象的类。 When loading objects from disk, you must start from the asumption that you GENERATED unique ids, so when loading them you don't have to deal with checking unicity again. 从磁盘加载对象时,必须从生成唯一ID的假设开始,因此在加载它们时,不必再次检查唯一性。

On your last remark about using generics, I guess you could do this: 关于使用泛型的最后一句话,我想您可以这样做:

abstract class UniqueObject<T> where T : class

And then 接着

items.Add(this as T);

This should work, and this as T should never fail on runtime if you don't explicitly use UniqueObject<> . 这应该可以工作,并且如果您不显式使用UniqueObject<>那么this as T应该永远不会在运行时失败。

I'm not sure about how I feel about advocating having static members on generic types ( and you should not do that ), but this should at least work 我不确定我对泛型类型拥有静态成员的感觉( 您不应该这样做 ),但这至少应该有效

Update : yes, it seems to work 更新 :是的, 似乎可行

Recommendation 建议

In my answer, I tried to answer exactly what you were asking. 在我的回答中,我试图准确回答您的要求。 But with all this said, if all you need is a unique ID for your objects and checking if it's not duplicated when you create them, you could: 综上所述,如果您需要的只是对象的唯一ID,并检查在创建对象时是否没有重复的ID,则可以:

  1. Use a GUID, and forget about checking. 使用GUID,而不必进行检查。 GUID collisions are theoretically possible.... but will it happen? GUID冲突在理论上是可能的...。但是会发生吗? Under normal conditions, more likely not. 在正常情况下,更有可能不会。 Even if you created a trillion GUIDs in a year, there's a higher chance your program will crash by a meteorite striking the computer several times before you find a duplicate 即使您一年内创建了上万亿的GUID,也有很大的机会您的程序会因陨石撞击计算机多次而崩溃,然后再找到一个重复的GUID。

  2. Still, if you want to check it and make absolutely sure (which is a fair thing to do, actually), this could be way easier, and you don't need to store a list of the whole objects per-type to do this... see this simple base class, which will do what you want in a similar way you are doing it: 不过,如果您要检查并绝对确定(实际上这是一件公平的事情),这可能会更容易,并且您不需要按类型存储整个对象的列表即可执行此操作...参见这个简单的基类,它将以与您执行操作类似的方式执行您想要的操作:

     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); } } 

And that's it. 就是这样。 If you still want to use int instead of Guid , you could just change it to int , and have something like: 如果仍然要使用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));

Which looks similar to what you had before 看起来和以前一样

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM