65.9K
CodeProject 正在变化。 阅读更多。
Home

另一套 C#,这次是泛型的

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.64/5 (11投票s)

2006年2月19日

CPOL

2分钟阅读

viewsIcon

35901

downloadIcon

138

如何在 C# 中为 .NET Framework 2.0 轻松实现通用集合

引言

大家好,有兴趣的人,我希望这是一个完全功能的“Delphi”集合实现。

背景

我真的需要在 C# 中使用一些像样的集合,并且我在这里看到了两篇非常好的文章。但那些并没有让我满意,所以我编写了自己的通用集合..这次我希望这是一个明确的版本。

让我们从某个地方开始

带着我特有的傲慢,让我们在这里实现这个类:(当然,你可以根据自己的喜好命名它,但我觉得这样非常实用:)

using System;
using System.Collections;
using System.Collections.Generic;

namespace System.Collections.Generic
{
    public class Set<T> : IEnumerable<T>
    {

现在我们需要一些东西来存储“集合”中的值

private Dictionary<T, T> fItems = 
               new Dictionary<T, T>();

这很好,但要制作一个功能性的集合还有很多事情要做。例如,重写 C# 运算符以获得我们应得的所有舒适感(以及微软拒绝我们的舒适感)。

所以让我们一个接一个地来

通过这种方式,我们的“将元素包含到集合中”的运算符可能会被实现

public static Set<T> operator +(Set<T> pSource, T pElement)
{
    try
    {
        Set<T> result = new Set<T>();
        result = (Set<T>)pSource.MemberwiseClone();
        result.fItems[pElement] = default(T);
        return result;
    }
    catch (Exception E)
    {
        throw new Exception("Set<T> - INCLUDE" + 
              " operation : Error while" + 
              " including element into set", E);
    }
}

你认为这不属于 C# 语言吗?你认为这不属于 C# 代码吗?而且它永远不会编译?好吧,我第一次也是这么认为的。

让我们回到我们的代码。现在我们可能想实现一个“排除”运算符(它很相似)

public static Set<T> operator -(Set<T> pSource, T pElement)
{
    try
    {
        Set<T> result = new Set<T>();
        result = (Set<T>)pSource.MemberwiseClone();
        pSource.fItems.Remove(pElement);
        return result;
    }
    catch (Exception E)
    {
        throw new Exception("Set<T> - EXCLUDE" + 
              " operation : Error while" + 
              " excluding element from set", E);
    }
}

当然,如果没有“包含”运算符及其对应的“不包含”运算符,它就不能称为集合

public static Boolean operator ==(Set<T> pSource, T pElement)
{
    return pSource.fItems.ContainsKey(pElement);
}
   
public static Boolean operator !=(Set<T> pSource, T pElement)
{
    return !(pSource == pElement);
}

你可能会说这些很容易,然后我不得不告诉你..是的,它们确实是。好的。现在这里有一些真正的代码。因为我们仍然需要集合运算符。它们来了。作为第一个“并集”运算符

public static Set<T> operator +(Set<T> pSource, 
                                   Set<T> pDestiny)
{
     Set<T> result = new Set<T>();
     IEnumerator<T> listEnum = null;

     try
     {
         listEnum = pSource.fItems.Keys.GetEnumerator();
         listEnum.Reset();


         while (listEnum.MoveNext())
         {
             result.fItems[listEnum.Current] = default(T);
         }
     }
     catch (Exception E)
     {
         throw new Exception("Set<T> -" + 
               " UNION operation : Error while" + 
               " joining first set", E);
     }
     
     try
     {
         listEnum = pDestiny.fItems.Keys.GetEnumerator();
         listEnum.Reset();

         while (listEnum.MoveNext())
         {
             if (!pSource.fItems.ContainsKey(listEnum.Current))
             {
                 result.fItems[listEnum.Current] = default(T);
             }
         }
     }
     catch (Exception E)
     {
         throw new Exception("Set<T> - UNION" + 
               " operation : Error while" + 
               " joining second set", E);
     }
     
     return result;
}

然后是..是的,这是一个“差集”运算符

public static Set<T> operator -(Set<T> pSource, 
                                   Set<T> pDestiny)
 {
     Set<T> result = new Set<T>();
     IEnumerator<T> listEnum = null;
     
     try
     {
         listEnum = pSource.fItems.Keys.GetEnumerator();
         listEnum.Reset();
         
         while (listEnum.MoveNext())
         {
             if (!pDestiny.fItems.ContainsKey(listEnum.Current))
             {
                 result.fItems[listEnum.Current] = default(T);
             }
         }
     }
     catch (Exception E)
     {
         throw new Exception("Set<T> - DIFFERENCE" + 
               " operation : Error while forward" + 
               " comparing two sets for difference", E);
     }
     
     try
     {
         listEnum = pDestiny.fItems.Keys.GetEnumerator();
         listEnum.Reset();
         
         while (listEnum.MoveNext())
         {
             if (!pSource.fItems.ContainsKey(listEnum.Current))
             {
                 result.fItems[listEnum.Current] = default(T);
             }
         }
     }
     catch (Exception E)
     {
         throw new Exception("Set<T> - DIFFERENCE" + 
               " operation : Error while backward " + 
               "comparing two sets for difference", E);
     }
     
     return result;
}

最后但并非最不重要的是一个“交集”运算符

public static Set<T> operator *(Set<T> pSource, 
                                   Set<T> pDestiny)
{
     Set<T> result = new Set<T>();
     IEnumerator<T> listEnum = null;
     
     try
     {
         listEnum = pSource.fItems.Keys.GetEnumerator();
         listEnum.Reset();
       
         while (listEnum.MoveNext())
         {
             if (pDestiny.fItems.ContainsKey(listEnum.Current))
             {
                 result.fItems[listEnum.Current] = default(T);
             }
         }
     }
     catch (Exception E)
     {
         throw new Exception("Set<T> - INTERSECTION" + 
               " operation : Error while comparing " + 
               "two sets for intersection", E);
     }
  
     return result;
}

现在来点精华

好吧,我们已经完成了基础工作,现在..既然你们都是那么好的观众,我就给你们更多:P 我发现一个非常好的事情是为集合建立索引,因为有时你只想知道集合中有什么元素,并且遍历所有元素并测试它们是否在集合中是很无聊的。你们肯定都知道我要说什么。所以现在我们准备实现一个索引器

public T this[int ItemIndex]
{
     get
     {
         if (ItemIndex < fItems.Keys.Count)
         {
             IEnumerator<T> listEnum = fItems.Keys.GetEnumerator();
             listEnum.Reset();
             
             for (int a = 0; a < fItems.Keys.Count; a++)
             {
                 if (a == ItemIndex)
                 {
                     return listEnum.Current;
                 }
                 listEnum.MoveNext();
             }
         }
      
         return default(T);
     } 
}

另一件好事是,在使用这些类时,周围有一些智能构造函数。这些就可以了

public static Set<T> Empty()
{
     return new Set<T>();
}

public static Set<T> Define(params T[] pElements)
{
     Set<T> result = new Set<T>();
    
     foreach (T element in pElements)
     {
         result += element;
     }
 
     return result;
}

好的,妈妈,我来做!

所以我们已经成功地添加了一些运算符和一些特殊的东西,接下来是什么..好吧,我不会骗你,这就是全部。但是我们仍然需要实现一些次要的“可能/必须”的事情。例如,实现我们一开始声明的接口

public IEnumerator<T> GetEnumerator()
{
     return fItems.Keys.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
     return fItems.Keys.GetEnumerator();
}

当然,重写一些基本方法也是一个好习惯。因为我们不希望警告到处乱飞

public override bool Equals(object obj)
{
     return (this == (Set<T>)obj);
}

public override int GetHashCode()
{
     return base.GetHashCode();
}

public override string ToString()
{
     String result = string.Empty;
     IEnumerator<T> listEnum = null;
     
     try
     {
         listEnum = fItems.Keys.GetEnumerator();
         listEnum.Reset();
    
         while (listEnum.MoveNext())
         {
             if (result == string.Empty)
             {
                 result = listEnum.Current.ToString();
             }
             else
             {
                 result += "," + listEnum.Current.ToString();
             }
         }
        
         return ("{" + result + "}");
     }
     catch (Exception E)
     {
         throw new Exception("Set<T> - TOSTRING" + 
               " operation : Error while exporting" + 
               " set to string", E);
     }
}

还有些东西不见了

让我们看一些例子,看看这些小野兽是如何使用的

// our testing sets make your Set<YourType>

Set<MyEnum> first = 
      Set<MyEnum>.Define(new MyEnum[] 
      { MyEnum.One, MyEnum.Two });
Set<MyEnum> second = 
      Set<MyEnum>.Define(new MyEnum[] 
      { MyEnum.Two, MyEnum.Three });
Set<MyEnum> result = null;

// flag of containment

Boolean contains = false;
result = first + second; // UNION

// now the result contains {One, Two, Three}
result = first - second; // DIFFERENCE

// now the result contains {One, Three}
result = first * second; // INTERSECTION

// now the result contains {Two} 
contains = first == MyEnum.Three;

// contains is now FALSE because
// it doesn't contain {Three}
 
first += MyEnum.Three; // INCLUDE
contains = first == MyEnum.Three;

// contains is now TRUE because
// we added {Three} before

first -= MyEnum.Three;
contains = first == MyEnum.Three;

// contains is now FALSE again because
// we deleted {Three} element

今天就到这里,我希望你发现这些集合对你来说很实用,或者它们可能会启发你制作一些更好的集合。我将查看论坛 :))

© . All rights reserved.