0

String类代表“字符的集合”并且是不可变的。它的索引器只定义了 get 函数,这没关系,因为Char“struct”也是不可变的。所有String用于操作的方法都返回String类的新实例。

我最近需要一个不可变的通用集合,它与(我们称之为)完全一样。StringFoo<T>

  • 它应该是通用的(尽管我只会将它与结构一起使用)。
  • 它应该是不可变的。
  • 它应该具有用于​​项目序列的方法,例如:
    • IndexOf(Foo<T>)或者IndexOf(IEnumerable<T>)
    • StartsWith(Foo<T>)或者StartsWith(IEnumerable<T>)
    • EndsWith(Foo<T>)或者EndsWith(IEnumerable<T>)
    • Take(int, int)(带有起始索引和长度,就像Substring
    • Contains(Foo<T>)或者Contains(IEnumerable<T>)
    • LastIndexOf(Foo<T>)或者LastIndexOf(IEnumerable<T>)
    • 等等

我创建了一个不可变类,用于对其项目的只读访问,并编写了一些扩展方法来模仿 String 的功能,但我真的怀疑我的实现效率(我实际上要求Replace方法,here)。我对替代品很好奇。因为String做了我需要的一切(不幸的是,只有字符),所以感觉就像重新发明了轮子。

我需要的最简单的定义是“通用字符串”。

  • .NET 中有类似的东西还是为.NET 编写的?
  • 如果没有,一些创建指南会很棒。

回答和评论后编辑:

我需要的不是包装指定的底层可变集合并将其表示为只读的包装器。我需要的是一个真正不可变的Twith 方法集合来处理T. IList<T>.IndexOf(T)例如,它获取item的索引。现在想想String.IndexOf(String)方法,它(不像 的IndexOf(Char)方法String)获取一个字符序列String的索引并且有很多这样的方法。

现在,为什么我不使用ReadOnlyCollection<T>:除了它不支持“ (String-like) 方法,例如 Contains(IEnumerable) ”之外,它也不是不可变的。一个例子:

var array = new char[] { 'a', 'b', 'c', 'd', 'e' };
var str = new string(array);
// array[2] is 'c' and str[2] is also 'c'
// I can't do str[2] = 'f', but:
array[2] = 'f';
// Now, array[2] is 'f' but str[2] is still 'c'

没有办法(这不是黑客)来改变字符串的状态。现在,让我们来看看ReadOnlyCollection<T>

var array = new int[] { 1, 2, 3, 4, 5 };
var col = new ReadOnlyCollection<int>(array);
// Here the col[2] is 3
// I can't do col[2] = 6, but:
array[2] = 6;
// Now the col[2] is 6 as well.

根据要求进行编辑 - 我目前使用的是:

集合(Foo<T>):

// Something I started like an hour ago. The only thing it does right now is to
// copy (not wrap) a specified enumerable and provide read-only access to it.
public sealed class Foo<T> : IList<T> where T: struct
{
    private readonly T[] _Array;

    public T this[int index] { get { return _Array[index]; } }
    IList<T>.this[int index]
    {
        get { return this[index]; }
        set { throw new NotSupportedException(); }
    }
    public Foo(IEnumerable<T> collection)
    {
        // Enumerable.ToArray() method copies the content of the specified array.
        // Whetever happens to the "collection", value of "_Array" will stay the same. 
        _Array = collection.ToArray();
    }

    // Most of the methods of IList<T> are explicitly implemented. IsReadOnly
    // returns true and the methods that cause a change in collection throw
    // "NotSupportedException"s just like ReadOnlyCollection<T>.
    // IEnumerable<T> implementation uses an iterator block.
}

扩展方法:

// Extensions I used to manipulate collections so far.
// These are the things I want to get rid of.
public static class FooHelpers
{
     // I remove the bodies of these methods due to the confusion they have caused.
     // How they work is irrelevant and I posted these because of a request.
     public static bool Contains<T>(this IEnumerable<T> collection,
         IList<T> pattern) { }
     public static int IndexOf<T>(this IEnumerable<T> collection,
         IList<T> pattern) { }
     public static int LastIndexOf<T>(this IList<T> collection,
         IList<T> pattern) { }
     public static IEnumerable<int> IndicesOf<T>(this IEnumerable<T> collection,
         IList<T> pattern) { }
     public static IEnumerable<int> LastIndicesOf<T>(this IList<T> collection,
         IList<T> pattern) { }
     public static IEnumerable<T[]> Split(this IList<T> source,
         IList<T> seperator) { }
     public static bool StartsWith<T>(this IEnumerable<T> collection,
         IList<T> pattern) { }
     public static bool EndsWith<T>(this IList<T> collection,
         IList<T> pattern) { }
     public static IEnumerable<T> Take<T>(this IList<T> collection,
         int startIndex,
         int length) { }
     public static IEnumerable<T> Take<T>(this IEnumerable<T> collection,
         int startIndex,
         int length) { }
     public static IEnumerable<T> TakeAll<T>(this IList<T> collection,
         int startIndex) { }
}
4

4 回答 4

1

这里似乎有2个问题:

1) 创建不可变集合

简短的回答是No,对此没有内置支持。

最接近的答案确实是 a ReadOnlyCollection,您可以创建一个简单的包装器

public class ImmutableCollection<T> : ReadOnlyCollection<T> {

  public ImmutableCollection(IEnumerable<T> source) : base(source.ToList()) {}

}

构造函数中的ToList调用会复制源集合,以便您可以修改源集合

如果失败,您将不得不实现自己的类,可能继承自IList<T>IEnumerable<T>并提供您自己的 get 访问器。

无论哪种方式,您都必须记住,每个T都不能保证是不可变的(即使使用结构,因为结构可能具有作为引用对象的字段成员)。

但是由于您仍然必须复制源集合以使您的集合不可变,因此最好使用第一个示例。

2) 提供额外的函数来执行类似字符串的操作。

您必须实现自己的功能:

  public bool Contains(IEnumerable<T> pattern) {
   return IndicesOf(pattern).Any();
 }           

 public int IndexOf(IEnumerable<T> pattern) {
   return IndicesOf(pattern).Select(x=>(int?)x).FirstOrDefault() ?? -1;
 }           

 public int LastIndexOf(IEnumerable<T> pattern) {
   return IndicesOf(pattern).Select(x=>(int?)x).LastOrDefault()?? -1;
 }           

 public IEnumerable<int> IndicesOf(IEnumerable <T> pattern) {
  var count=pattern.Count();
  return Enumerable.Range(0,this.Count()-count).Where(i=> pattern.SequenceEqual(internalTake(i,count)));
 }           

 public IEnumerable<int> LastIndicesOf(IEnumerable<T> pattern) {
   return IndicesOf(pattern).Reverse(); // Could Optimize
 }

 private IEnumerable<IEnumerable<T>> internalSplit(IEnumerable<T> seperator) {
   var splitPoints=this.IndicesOf(seperator);
   var length=seperator.Count();
   var lastCount=0;
   foreach(var point in splitPoints.Where(x=>!splitPoints.Any(y=>y<x && y+length>x))) {
        yield return this.Take(lastCount,point-lastCount);
        lastCount=point+length;
   }
   yield return this.TakeAll(lastCount);
 } 


 public ImmutableCollection<T>[] Split(IEnumerable<T> seperator) {
   return internalSplit(seperator).Select(x=>new ImmutableCollection<T>(x)).ToArray();
 }          

 public bool StartsWith(IEnumerable<T> pattern) {
    return pattern.SequenceEqual(this.Take(pattern.Count()));
 }           
 public bool EndsWith(IEnumerable<T> pattern) {
    return pattern.SequenceEqual(this.Skip(this.Count()-pattern.Count()));
 }           

 private IEnumerable<T> internalTake(int startIndex, int length) {
    var max=(length==-1) ? this.Count() : Math.Min(this.Count(),startIndex+length);
    for (int i=startIndex;i<max;i++) yield return this[i];
 }

 public ImmutableCollection<T> Take(int startIndex, int length) {
    return new ImmutableCollection<T>(internalTake(startIndex,length));
 }           

 public ImmutableCollection<T> TakeAll(int startIndex) {
    return new ImmutableCollection<T>(internalTake(startIndex,-1));
 }           
于 2012-08-14T09:05:16.303 回答
0

我认为这就是您要查找的内容: List.AsReadOnly http://msdn.microsoft.com/en-us/library/e78dcd75.aspx

于 2012-08-13T21:39:48.720 回答
0

如果您想要一种找到子序列的有效方法,那么最好的方法可能是为此目的编写您自己的集合。我建议将 aT[]与 an 结合使用int[]来存储每个值的哈希码。然后可以——在很大程度上——将查找序列的任务减少为T查找哈希值序列。可以将 N 个哈希码的序列转换为 3N 个字符的字符串,使用 3 个字符来存储每个哈希码,然后使用string.Contains或正则表达式解析器来执行序列查找。

于 2013-07-25T04:20:40.917 回答
-1

只读集合? http://msdn.microsoft.com/en-us/library/ms132474.aspx

于 2012-08-13T21:51:58.280 回答