0

Stringクラスは「文字のコレクション」を表し、不変です。インデクサーにはget関数のみが定義されており、Char「struct」も不変であるため問題ありません。操作に使用されるすべてのメソッドは、クラスStringの新しいインスタンスを返します。String

私は最近、まったく同じような不変のジェネリックコレクションが必要でしたString(それを呼びましょうFoo<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メソッドを要求しました)。代替案に興味があります。私が必要とするすべてのことを(残念ながら、charsだけに)行うのでString、それは車輪の再発明のように感じます。

私が必要とするものの最も簡単な定義は「一般的な文字列」です。

  • .NETにこのようなものや.NET用に書かれたものはありますか?
  • そうでない場合は、それを作成するためのいくつかのガイドラインが素晴らしいでしょう。

回答とコメントの後に編集します。

私が必要としているのは、指定された基になる可変コレクションをラップし、それを読み取り専用として表すラッパーではありません。私が必要としているのは、のシーケンスを処理するためのメソッドを備えた真に不変のコレクションです。たとえば、アイテムのインデックスを取得するとします。ここでメソッドを考えてみましょう。(のメソッドとは異なり)文字のシーケンスのインデックスを取得し、これらの種類のメソッドを多数備えています。TTIList<T>.IndexOf(T)String.IndexOf(String)IndexOf(Char)StringString

さて、私が使用しない理由:「 Contains(IEnumerable)」などの(文字列のような)メソッドをReadOnlyCollection<T>サポートしていないことを除けば、不変でもありません。例:

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)不変のコレクションを作成する

簡単な答えは「いいえ」です。これに対する組み込みのサポートはありません。

最も近い答えは本当に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

サブシーケンスを見つける効率的な手段が必要な場合は、その目的のために独自のコレクションを作成するのが最善の方法です。T[]と組み合わせて、int[]すべての値のハッシュコードを格納することをお勧めします。次に、かなりの程度まで、シーケンスを見つけるタスクをTハッシュ値のシーケンスを見つけるタスクに減らすことができます。N個のハッシュコードのシーケンスを3文字を使用して3N文字の文字列に変換し、各ハッシュコードを格納してから、string.Containsまたは正規表現パーサーを使用してシーケンスルックアップを実行できる場合があります。

于 2013-07-25T04:20:40.917 に答える
-1

ReadOnlyCollection? http://msdn.microsoft.com/en-us/library/ms132474.aspx

于 2012-08-13T21:51:58.280 に答える