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

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

我需要为可以在应用程序实例之间保存和加载的对象创建唯一 ID。

我在当前的实现中使用了这个,但这意味着我的每个 classes 都需要几乎相同的代码,因此我决定使用这段代码创建一个基础 class 和然后从中继承。代码如下。我遇到的唯一问题是因为我在基础 class 中有一个 静态列表 ,所有继承的 class 类型都被添加到同一个列表中。

因此,我怎样才能更改代码,使列表 'items' 成为不同类型的列表?

澄清一下。如果我有两个 classes 列出这个: Foo:UniqueObject 栏:UniqueObject 我想让 Foo 和 Bar 有自己的 static item list

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

我相信我可以使用泛型实现这一点,所以我可以更改 class 并列出如下内容:

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

但这会在行 items.Add(this).

中给出其他错误

如有任何帮助,我们将不胜感激。

如果您想要一个具有以下属性的唯一 ID:

1) 在当前应用域中是唯一的

2) 即使在处理应用程序的多个实例时,值也是唯一的。

那么您需要考虑以下解决方案之一:

1) 生成 GUIDS

2) 为您生成的 ID 设置一个唯一的 "server"(可以为您的 ID 提供服务的通用服务器)

3) 如果您确切知道自己有多少个应用程序实例,则可以为每个实例定义 "series" 个唯一 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 定义了一个 "unique" 对象在你的应用程序中的样子,所以它是一个具有 UniqueId 属性 的对象,还有一个特殊的方法:SetUniqueId。我没有使用 get 和 set 创建 属性 UniqueId,因为 "set" 将是基础架构操作,但 get 将是开发人员 API.

2) IUniqueIdsProvider 接口告诉您唯一 ID 提供程序的外观。它必须有一个简单的方法:GetNewId();为您提供唯一的 ID。实现可以在任何地方(在服务器上、本地等)

3) UniqueObject class。此 class 是您所有独特对象的基础 class。

4) UniqueObjectsFactory。这是为您提供新的独特对象的 class。从磁盘加载对象时,您必须从生成唯一 ID 的假设开始,因此在加载它们时您不必再次处理检查唯一性。

关于你关于使用泛型的最后评论,我想你可以这样做:

abstract class UniqueObject<T> where T : class

然后

items.Add(this as T);

这应该有效,如果您没有明确使用 UniqueObject<>.

this as T 应该永远不会在运行时失败

我不确定我对提倡在泛型上使用静态成员有何感受 (and you should not do that),但这至少应该有效

更新:是的,它seems to work

推荐

在我的回答中,我试图准确回答您的问题。但是综上所述,如果您只需要对象的唯一 ID 并在创建它们时检查它是否不重复,您可以:

  1. 使用 GUID,忘记检查。 GUID 冲突在理论上是可能的……但它会发生吗?在正常情况下,更有可能不会。即使您在一年内创建了一万亿个 GUID,在您找到重复项

    [=50 之前,您的程序更有可能因陨石撞击计算机 多次 而崩溃=]
  2. 不过,如果你想检查它并绝对确定(实际上这是一件公平的事情),这可能会更容易,而且你不需要存储列表每种类型的整个对象中的一部分来执行此操作...请参阅这个简单的基础 class,它将以与您类似的方式执行您想要的操作:

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

这看起来与您之前的相似