web-dev-qa-db-ja.com

これまでに見た拡張メソッドの最良または最も興味深い使用法は何ですか?

私は拡張メソッドが本当に好きになり始めています...彼女が本当に彼らの心を吹き飛ばしたものに出くわした人、または単に賢いと思った人はいないかと思いました。

私が今日書いた例:

他のユーザーのコメントにより編集:

public static IEnumerable<int> To(this int fromNumber, int toNumber) {
    while (fromNumber < toNumber) {
        yield return fromNumber;
        fromNumber++;
    }
}

これにより、forループをforeachループとして記述できます。

foreach (int x in 0.To(16)) {
    Console.WriteLine(Math.Pow(2, x).ToString());
}

他の例を見るのが待ちきれません!楽しい!

71
Mark Carpenter

これは最近私からいくつかの遊びを得ているものです:

public static IDisposable Tag(this HtmlHelper html, string tagName)
{
    if (html == null)
        throw new ArgumentNullException("html");

    Action<string> a = tag => html.Write(String.Format(tag, tagName));
    a("<{0}>");
    return new Memento(() => a("</{0}>"));
}

次のように使用されます:

using (Html.Tag("ul"))
{
    this.Model.ForEach(item => using(Html.Tag("li")) Html.Write(item));
    using(Html.Tag("li")) Html.Write("new");
}

Mementoは便利なクラスです:

public sealed class Memento : IDisposable
{
    private bool Disposed { get; set; }
    private Action Action { get; set; }

    public Memento(Action action)
    {
        if (action == null)
            throw new ArgumentNullException("action");

        Action = action;
    }

    void IDisposable.Dispose()
    {
        if (Disposed)
            throw new ObjectDisposedException("Memento");

        Disposed = true;
        Action();
    }
}

そして、依存関係を完了するには:

public static void Write(this HtmlHelper html, string content)
{
    if (html == null)
        throw new ArgumentNullException("html");

    html.ViewContext.HttpContext.Response.Write(content);
}
18
Talljoe

完全なソリューションは大きすぎてここに置くことはできませんが、DataTableをCSVに簡単に変換できる一連の拡張メソッドを作成しました。

public static String ToCSV(this DataTable dataTable)
{
    return dataTable.ToCSV(null, COMMA, true);
}  

public static String ToCSV(this DataTable dataTable, String qualifier)
{
    return dataTable.ToCSV(qualifier, COMMA, true);
}

private static String ToCSV(this DataTable dataTable, String qualifier, String delimiter, Boolean includeColumnNames)
{
    if (dataTable == null) return null;

    if (qualifier == delimiter)
    {
        throw new InvalidOperationException(
            "The qualifier and the delimiter are identical. This will cause the CSV to have collisions that might result in data being parsed incorrectly by another program.");
    }

    var sbCSV = new StringBuilder();

    var delimiterToUse = delimiter ?? COMMA;

    if (includeColumnNames) 
        sbCSV.AppendLine(dataTable.Columns.GetHeaderLine(qualifier, delimiterToUse));

    foreach (DataRow row in dataTable.Rows)
    {
        sbCSV.AppendLine(row.ToCSVLine(qualifier, delimiterToUse));
    }

    return sbCSV.Length > 0 ? sbCSV.ToString() : null;
}

private static String ToCSVLine(this DataRow dataRow, String qualifier, String delimiter)
{
    var colCount = dataRow.Table.Columns.Count;
    var rowValues = new String[colCount];

    for (var i = 0; i < colCount; i++)
    {
        rowValues[i] = dataRow[i].Qualify(qualifier);
    }

    return String.Join(delimiter, rowValues);
}

private static String GetHeaderLine(this DataColumnCollection columns, String qualifier, String delimiter)
{
    var colCount = columns.Count;
    var colNames = new String[colCount];

    for (var i = 0; i < colCount; i++)
    {
        colNames[i] = columns[i].ColumnName.Qualify(qualifier);
    }

    return String.Join(delimiter, colNames);
}

private static String Qualify(this Object target, String qualifier)
{
    return qualifier + target + qualifier;
}

結局のところ、次のように呼ぶことができます。

someDataTable.ToCSV(); //Plain old CSV
someDataTable.ToCSV("\""); //Double quote qualifier
someDataTable.ToCSV("\"", "\t"); //Tab delimited
18
Josh

私は、プロパティ名を文字列として渡す必要があるINotifyPropertyChangedインターフェイスのファンではありません。コンパイル時に、存在するプロパティのプロパティ変更のみを発生させて処理していることを確認する、厳密に型指定された方法が必要です。私はそれを行うためにこのコードを使用します:

public static class INotifyPropertyChangedExtensions
{
    public static string ToPropertyName<T>(this Expression<Func<T>> @this)
    {
        var @return = string.Empty;
        if (@this != null)
        {
            var memberExpression = @this.Body as MemberExpression;
            if (memberExpression != null)
            {
                @return = memberExpression.Member.Name;
            }
        }
        return @return;
    }
}

INotifyPropertyChangedを実装するクラスには、次のヘルパーメソッドを含めます。

protected void NotifySetProperty<T>(ref T field, T value,
    Expression<Func<T>> propertyExpression)
{
    if (field == null ? value != null : !field.Equals(value))
    {
        field = value;
        this.NotifyPropertyChanged(propertyExpression.ToPropertyName());
    }
}

最終的に私はこの種のことをすることができます:

private string _name;
public string Name
{
    get { return _name; }
    set { this.NotifySetProperty(ref _name, value, () => this.Name); }
}

強く型付けされており、実際に値を変更するプロパティのイベントのみを発生させます。

13
Enigmativity

これは正確には賢いわけではありませんが、コードの後半でnullをチェックする代わりに、デフォルトのアイテムをインラインで指定できるように---- OrDefaultメソッドを変更しました。

    public static T SingleOrDefault<T> ( this IEnumerable<T> source, 
                                    Func<T, bool> action, T theDefault )
    {
        T item = source.SingleOrDefault<T>(action);

        if (item != null)
            return item;

        return theDefault;
    }

その信じられないほどシンプルですが、本当にそれらのヌルチェックをクリーンアップするのに役立ちます。 UIがトーナメントシステムやゲームプレーヤースロットなどのXアイテムのリストを期待していて、「空席」を表示したい場合に最適です。

使用法:

    return jediList.SingleOrDefault( 
                 j => j.LightsaberColor == "Orange", 
               new Jedi() { LightsaberColor = "Orange", Name = "DarthNobody");
12
John Farrell

これが私が一緒にハッキングしたものですので、気軽に穴を開けてください。整数の(順序付けられた)リストを受け取り、連続する範囲の文字列のリストを返します。例えば:

1,2,3,7,10,11,12  -->  "1-3","7","10-12"

関数(静的クラス内):

public static IEnumerable<string> IntRanges(this IEnumerable<int> numbers)
{
    int rangeStart = 0;
    int previous = 0;

    if (!numbers.Any())
        yield break;

    rangeStart = previous = numbers.FirstOrDefault();

    foreach (int n in numbers.Skip(1))
    {
        if (n - previous > 1) // sequence break - yield a sequence
        {
            if (previous > rangeStart)
            {
                yield return string.Format("{0}-{1}", rangeStart, previous);
            }
            else
            {
                yield return rangeStart.ToString();
            }
            rangeStart = n;
        }
        previous = n;
    }

    if (previous > rangeStart)
    {
        yield return string.Format("{0}-{1}", rangeStart, previous);
    }
    else
    {
        yield return rangeStart.ToString();
    }
}

使用例:

this.WeekDescription = string.Join(",", from.WeekPattern.WeekPatternToInts().IntRanges().ToArray());

このコードは、DailyWTFに値するタイムテーブルアプリケーションからのデータを変換するために使用されます。 WeekPatternは、文字列「0011011100 ...」に格納されているビットマスクです。 WeekPatternToInts()は、これをIEnumerable <int>(この場合は[3,4,6,7,8])に変換し、「3-4,6-8」になります。講義が行われるアカデミックウィーク範囲のコンパクトな説明をユーザーに提供します。

11
geofftnz

私が使用したい2つは、私が作成したInsertWhere<T>とRemoveWhere<T>拡張メソッドです。 WPFおよびSilverlightでObservableCollectionsを操作する場合、順序付きリストを再作成せずに変更する必要があることがよくあります。これらのメソッドを使用すると、提供されたFuncに従って挿入および削除できるため、.OrderBy()を再度呼び出す必要はありません。

    /// <summary>
    /// Removes all items from the provided <paramref name="list"/> that match the<paramref name="predicate"/> expression.
    /// </summary>
    /// <typeparam name="T">The class type of the list items.</typeparam>
    /// <param name="list">The list to remove items from.</param>
    /// <param name="predicate">The predicate expression to test against.</param>
    public static void RemoveWhere<T>(this IList<T> list, Func<T, bool> predicate)
    {
        T[] copy = new T[] { };
        Array.Resize(ref copy, list.Count);
        list.CopyTo(copy, 0);

        for (int i = copy.Length - 1; i >= 0; i--)
        {
            if (predicate(copy[i]))
            {
                list.RemoveAt(i);
            }
        }
    }

    /// <summary>
    /// Inserts an Item into a list at the first place that the <paramref name="predicate"/> expression fails.  If it is true in all cases, then the item is appended to the end of the list.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="obj"></param>
    /// <param name="predicate">The sepcified function that determines when the <paramref name="obj"/> should be added. </param>
    public static void InsertWhere<T>(this IList<T> list, T obj, Func<T, bool> predicate)
    {
        for (int i = 0; i < list.Count; i++)
        { 
            // When the function first fails it inserts the obj paramiter. 
            // For example, in a list myList of ordered Int32's {1,2,3,4,5,10,12}
            // Calling myList.InsertWhere( 8, x => 8 > x) inserts 8 once the list item becomes greater then or equal to it.
            if(!predicate(list[i]))
            {
                list.Insert(i, obj);
                return;
            }
        }

        list.Add(obj);
    }

編集:
Talljoeは、私が急いで作成したRemoveWhere/RemoveAllにいくつかの重要な改善を加えました。 RemoveWhereの数秒(私はそれを待つのにうんざりしていました)とは対照的に、〜3millアイテムが3つおきに削除されるので、新しいバージョンは〜50ミリ秒(List.RemoveAllを呼び出すことができる場合は10ミリ秒未満!)しかかかりません。

これが彼の大幅に改良されたバージョンです、ありがとう!

    public static void RemoveAll<T>(this IList<T> instance, Predicate<T> predicate)
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        if (predicate == null)
            throw new ArgumentNullException("predicate");
        if (instance is T[])
            throw new NotSupportedException();

        var list = instance as List<T>;
        if (list != null)
        {
            list.RemoveAll(predicate);
            return;
        }

        int writeIndex = 0;
        for (int readIndex = 0; readIndex < instance.Count; readIndex++)
        {
            var item = instance[readIndex];
            if (predicate(item)) continue;

            if (readIndex != writeIndex)
            {
                instance[writeIndex] = item;
            }
            ++writeIndex;
        }

        if (writeIndex != instance.Count)
        {
            for (int deleteIndex = instance.Count - 1; deleteIndex >= writeIndex; --deleteIndex)
            {
                instance.RemoveAt(deleteIndex);
            }
        }
    }
11
rmoore

いろいろあります.Debugifyオブジェクトをログファイルにダンプするのに役立つ拡張メソッド。たとえば、これが私の辞書のデバッグです(リスト、Datatable、param配列などにこれらがあります):

public static string Debugify<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) {
    string Result = "";

    if (dictionary.Count > 0) {
        StringBuilder ResultBuilder = new StringBuilder();

        int Counter = 0;
        foreach (KeyValuePair<TKey, TValue> Entry in dictionary) {
            Counter++;
            ResultBuilder.AppendFormat("{0}: {1}, ", Entry.Key, Entry.Value);
            if (Counter % 10 == 0) ResultBuilder.AppendLine();
        }
        Result = ResultBuilder.ToString();
    }
    return Result;
}

そして、これがDbParameterCollection用のものです(データベース呼び出しをログファイルにダンプするのに便利です):

public static string Debugify(this DbParameterCollection parameters) {
    List<string> ParameterValuesList = new List<string>();

    foreach (DbParameter Parameter in parameters) {
        string ParameterName, ParameterValue;
        ParameterName = Parameter.ParameterName;

        if (Parameter.Direction == ParameterDirection.ReturnValue)
            continue;

        if (Parameter.Value == null || Parameter.Value.Equals(DBNull.Value))
            ParameterValue = "NULL";
        else
        {
            switch (Parameter.DbType)
            {
                case DbType.String:
                case DbType.Date:
                case DbType.DateTime:
                case DbType.Guid:
                case DbType.Xml:
                    ParameterValue
                        = "'" + Parameter
                                .Value
                                .ToString()
                                .Replace(Environment.NewLine, "")
                                .Left(80, "...") + "'"; // Left... is another Nice one
                    break;

                default:
                    ParameterValue = Parameter.Value.ToString();
                    break;
            }

            if (Parameter.Direction != ParameterDirection.Input)
                ParameterValue += " " + Parameter.Direction.ToString();
        }

        ParameterValuesList.Add(string.Format("{0}={1}", ParameterName, ParameterValue));
    }

    return string.Join(", ", ParameterValuesList.ToArray());
}

結果の例:

Log.DebugFormat("EXEC {0} {1}", procName, params.Debugify);
// EXEC spProcedure @intID=5, @nvName='Michael Haren', @intRefID=11 OUTPUT

これをafterDB呼び出しの後に呼び出すと、出力パラメーターも入力されることに注意してください。 SP nameを含む行でこれを呼び出すので、デバッグのために呼び出しをコピーしてSSMSに貼り付けることができます。


これらにより、コードを中断することなく、ログファイルをきれいに簡単に生成できます。

10
Michael Haren

ベース36の文字列(!)を整数に変換するための拡張メソッドのペア:

public static int ToBase10(this string base36)
{
    if (string.IsNullOrEmpty(base36))
        return 0;
    int value = 0;
    foreach (var c in base36.Trim())
    {
        value = value * 36 + c.ToBase10();
    }
    return value;
}

public static int ToBase10(this char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    c = char.ToUpper(c);
    if (c >= 'A' && c <= 'Z')
        return c - 'A' + 10;
    return 0;
}

(一部の天才は、データベースに数値を格納する最善の方法は、数値を文字列にエンコードすることであると判断しました。小数はスペースを取りすぎます。16進数の方が優れていますが、文字GZを使用しません。したがって、明らかに、base-16をbase-36に拡張します。 )

9
geofftnz

ADO.NETオブジェクトとメソッドの操作を簡単にするために、一連の拡張メソッドを作成しました。

1つの命令でDbConnectionからDbCommandを作成します。

    public static DbCommand CreateCommand(this DbConnection connection, string commandText)
    {
        DbCommand command = connection.CreateCommand();
        command.CommandText = commandText;
        return command;
    }

DbCommandにパラメーターを追加します。

    public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType)
    {
        DbParameter p = AddParameter(command, name, dbType, 0, ParameterDirection.Input);
        return p;
    }

    public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, object value)
    {
        DbParameter p = AddParameter(command, name, dbType, 0, ParameterDirection.Input);
        p.Value = value;
        return p;
    }

    public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, int size)
    {
        return AddParameter(command, name, dbType, size, ParameterDirection.Input);
    }

    public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, int size, ParameterDirection direction)
    {
        DbParameter parameter = command.CreateParameter();
        parameter.ParameterName = name;
        parameter.DbType = dbType;
        parameter.Direction = direction;
        parameter.Size = size;
        command.Parameters.Add(parameter);
        return parameter;
    }

インデックスではなく名前でDbDataReaderフィールドにアクセスします。

    public static DateTime GetDateTime(this DbDataReader reader, string name)
    {
        int i = reader.GetOrdinal(name);
        return reader.GetDateTime(i);
    }

    public static decimal GetDecimal(this DbDataReader reader, string name)
    {
        int i = reader.GetOrdinal(name);
        return reader.GetDecimal(i);
    }

    public static double GetDouble(this DbDataReader reader, string name)
    {
        int i = reader.GetOrdinal(name);
        return reader.GetDouble(i);
    }

    public static string GetString(this DbDataReader reader, string name)
    {
        int i = reader.GetOrdinal(name);
        return reader.GetString(i);
    }

    ...

別の(無関係の)拡張メソッドを使用すると、WinFormsフォームとコントロールでDragMove操作(WPFなど)を実行できます ここを参照

7
Thomas Levesque

これは、イベントを発生させる前にnullチェックを一元化するための拡張メソッドです。

public static class EventExtension
{
    public static void RaiseEvent<T>(this EventHandler<T> handler, object obj, T args) where T : EventArgs
    {
        EventHandler<T> theHandler = handler;

        if (theHandler != null)
        {
            theHandler(obj, args);
        }
    }
}
5
Taylor Leese

多くの場合、列挙値に基づいてユーザーフレンドリーな値を表示する必要がありましたが、あまりエレガントに見えなかったため、カスタム属性ルートを使用したくありませんでした。

この便利な拡張方法を使用すると:

public static string EnumValue(this MyEnum e) {
    switch (e) {
        case MyEnum.First:
            return "First Friendly Value";
        case MyEnum.Second:
            return "Second Friendly Value";
        case MyEnum.Third:
            return "Third Friendly Value";
    }
    return "Horrible Failure!!";
}

私がすることができます:

Console.WriteLine(MyEnum.First.EnumValue());

わーい!

5
Mark Carpenter

非常に単純ですが、プロジェクトの100億回の完全な結果セットからページを取得するため、これは特に便利です。

public static class QueryableExtensions
{
    public static IQueryable<T> Page(this IQueryable<T> query, int pageNumber, int pageSize)
    {
        int skipCount = (pageNumber-1) * pageSize;
        query = query.Skip(skipCount);
        query = query.Take(pageSize);

        return query;
    }
}
5
jrista

ここで見た拡張メソッドのほとんどの例は、ベストプラクティスに反しています。拡張メソッドは強力ですが、慎重に使用する必要があります。私の経験では、これらのほとんどには、昔ながらの構文を持つ静的なヘルパー/ユーティリティクラスが一般的に好まれます。

列挙型の拡張メソッドにはメソッドを持つことができないため、言いたいことがあります。それらを列挙型と同じ名前空間および同じアセンブリで定義すると、それらは透過的に機能します。

5
Thorarin

これは信じられないほどシンプルですが、私がよく行うチェックなので、拡張メソッドを作成することになりました。私のお気に入りの拡張メソッドは、このような非常に単純で単純なもの、またはイベントを発生させるためのTaylorLの拡張メソッドのようなものになる傾向があります。

public static bool IsNullOrEmpty(this ICollection e)
{
    return e == null || e.Count == 0;
}
3
Sterno

より機能的なコンビネータコードを許可するには:

    public static Func<T, R> TryCoalesce<T, R>(this Func<T, R> f, R coalesce)
    {
        return x =>
            {
                try
                {
                    return f(x);
                }
                catch
                {
                    return coalesce;
                }
            };
    }
    public static TResult TryCoalesce<T, TResult>(this Func<T, TResult> f, T p, TResult coalesce)
    {
        return f.TryCoalesce(coalesce)(p);
    }

次に、次のように書くことができます。

    public static int ParseInt(this string str, int coalesce)
    {
        return TryCoalesce(int.Parse, str, coalesce);
    }
2
eulerfx

私がよく使用するもう1つのセットは、IDictionaryメソッドを合体させるためのものです。

    public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key, Func<TValue> valueThunk)
    {
        TValue v = d.Get(key);
        if (v == null)
        {
            v = valueThunk();
            d.Add(key, v);
        }
        return v;
    }
    public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key, TValue coalesce)
    {
        return Get(d, key, () => coalesce);
    }

そして、一般的にコレクションを操作するために:

    public static IEnumerable<T> AsCollection<T>(this T item)
    {
        yield return item;
    }

次に、木のような構造の場合:

    public static LinkedList<T> Up<T>(this T node, Func<T, T> parent)
    {
        var list = new LinkedList<T>();
        node.Up(parent, n => list.AddFirst(n));
        return list;
    }

したがって、次のようなクラスを簡単にトラバースして操作できます。

class Category
{
    public string Name { get; set; }
    public Category Parent { get; set; }
}

次に、関数の合成と、C#でのプログラミングのF#のような方法を容易にするために:

public static Func<T, T> Func<T>(this Func<T, T> f)
{
    return f;
}
public static Func<T1, R> Compose<T1, T2, R>(this Func<T1, T2> f, Func<T2, R> g)
{
    return x => g(f(x));
}
2
eulerfx

多くのJavaをC#に変換しています。メソッドの多くは、大文字と小文字の区別またはその他の小さな構文の違いのみが異なります。したがって、Java

myString.toLowerCase();

コンパイルされませんが、拡張メソッドを追加します

public static void toLowerCase(this string s)
{
    s.ToLower();
}

私はすべてのメソッドを捕まえることができます(そして私は良いコンパイラがとにかくこれをインライン化すると思いますか?)。

それは確かに仕事をはるかに簡単で信頼できるものにしました。 (@Yuriyに感謝します-次の回答を参照してください: JavaとC# のStringBuilderの違い))提案について。

2

私が主に使用するいくつかの拡張機能。最初のセットはオブジェクト拡張であり、実際には変換専用です。

public static class ObjectExtension
{
    public static T As<T>(this object value)
    {
        return (value != null && value is T) ? (T)value : default(T);
    }

    public static int AsInt(this string value)
    {
        if (value.HasValue())
        {
            int result;

            var success = int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);

            if (success)
            {
                return result;
            }
        }

        return 0;
    }

    public static Guid AsGuid(this string value)
    {
        return value.HasValue() ? new Guid(value) : Guid.Empty;
    }
}

文字列拡張子

public static class StringExtension
{
    public static bool HasValue(this string value)
    {
        return string.IsNullOrEmpty(value) == false;
    }

    public static string Slug(this string value)
    {
        if (value.HasValue())
        {
            var builder = new StringBuilder();
            var slug = value.Trim().ToLower();

            foreach (var c in slug)
            {
                switch (c)
                {
                    case ' ':
                        builder.Append("-");
                        break;
                    case '&':
                        builder.Append("and");
                        break;
                    default:

                        if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') && c != '-')
                        {
                            builder.Append(c);
                        }

                        break;
                }
            }

            return builder.ToString();
        }

        return string.Empty;
    }

    public static string Truncate(this string value, int limit)
    {
        return (value.Length > limit) ? string.Concat(value.Substring(0, Math.Min(value.Length, limit)), "...") : value;
    }
}

そして最後はいくつかの列挙型拡張です

public static class EnumExtensions
{
    public static bool Has<T>(this Enum source, params T[] values)
    {
        var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);

        foreach (var i in values)
        {
            var mask = Convert.ToInt32(i, CultureInfo.InvariantCulture);

            if ((value & mask) == 0)
            {
                return false;
            }
        }

        return true;
    }

    public static bool Has<T>(this Enum source, T values)
    {
        var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);
        var mask = Convert.ToInt32(values, CultureInfo.InvariantCulture);

        return (value & mask) != 0;
    }

    public static T Add<T>(this Enum source, T v)
    {
        var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);
        var mask = Convert.ToInt32(v, CultureInfo.InvariantCulture);

        return Enum.ToObject(typeof(T), value | mask).As<T>();
    }

    public static T Remove<T>(this Enum source, T v)
    {
        var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);
        var mask = Convert.ToInt32(v, CultureInfo.InvariantCulture);

        return Enum.ToObject(typeof(T), value & ~mask).As<T>();
    }

    public static T AsEnum<T>(this string value)
    {
        try
        {
            return Enum.Parse(typeof(T), value, true).As<T>();
        }
        catch
        {
            return default(T);
        }
    }
}
1
Mike Geise

StringBuilderを定期的に使用すると、AppendFormat()とAppendLine()を組み合わせる必要がある場合があります。

public static void AppendFormatLine(this StringBuilder sb, string format, params object[] args)
{
    sb.AppendFormat(format, args);
    sb.AppendLine();
}

また、アプリケーションをVB6からC#に変換しているので、次のことが非常に役立ちます。

public static string Left(this string s, int length)
{
    if (s.Length >= length)
        return s.Substring(0, length);
    throw new ArgumentException("Length must be less than the length of the string.");
}
public static string Right(this string s, int length)
{
    if (s.Length >= length)
        return s.Substring(s.Length - length, length);
    throw new ArgumentException("Length must be less than the length of the string.");
}
1
opedog

クールで愛情のある拡張機能!

ここにいくつかあります。

これは月の最後の日付を取得します:

<System.Runtime.CompilerServices.Extension()> _
    Public Function GetLastMonthDay(ByVal Source As DateTime) As DateTime
        Dim CurrentMonth As Integer = Source.Month
        Dim MonthCounter As Integer = Source.Month
        Dim LastDay As DateTime
        Dim DateCounter As DateTime = Source

        LastDay = Source

        Do While MonthCounter = CurrentMonth
            DateCounter = DateCounter.AddDays(1)
            MonthCounter = DateCounter.Month

            If MonthCounter = CurrentMonth Then
                LastDay = DateCounter
            End If
        Loop

        Return LastDay
    End Function

これらの2つは、反射を少し簡単にします。

 <System.Runtime.CompilerServices.Extension()> _
    Public Function GetPropertyValue(Of ValueType)(ByVal Source As Object, ByVal PropertyName As String) As ValueType
        Dim pInfo As System.Reflection.PropertyInfo

        pInfo = Source.GetType.GetProperty(PropertyName)

        If pInfo Is Nothing Then
            Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
        Else
            Return pInfo.GetValue(Source, Nothing)
        End If
    End Function

    <System.Runtime.CompilerServices.Extension()> _
    Public Function GetPropertyType(ByVal Source As Object, ByVal PropertyName As String) As Type
        Dim pInfo As System.Reflection.PropertyInfo

        pInfo = Source.GetType.GetProperty(PropertyName)

        If pInfo Is Nothing Then
            Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
        Else
            Return pInfo.PropertyType
        End If
    End Function
1
andy

私は これ が好きです。これはString.Splitメソッドのバリエーションであり、分割文字が実際の文字列に含まれることを意図している場合に、エスケープ文字を使用して分割を抑制することができます。

1
Robert Harvey

私が最もよく使用する拡張メソッドは、 System.Linq.Enumerable クラス。

そして、あなたが見つけることができるそのリストへの良いそして役に立つ拡張 MoreLinq

1
Svish

日(この場合は週の最初の日は月曜日)を指定するビットマスクをDayOfWeek列挙型の列挙にデコードするintの拡張メソッド:

public static IEnumerable<DayOfWeek> Days(this int dayMask)
{
    if ((dayMask & 1) > 0) yield return DayOfWeek.Monday;
    if ((dayMask & 2) > 0) yield return DayOfWeek.Tuesday;
    if ((dayMask & 4) > 0) yield return DayOfWeek.Wednesday;
    if ((dayMask & 8) > 0) yield return DayOfWeek.Thursday;
    if ((dayMask & 16) > 0) yield return DayOfWeek.Friday;
    if ((dayMask & 32) > 0) yield return DayOfWeek.Saturday;
    if ((dayMask & 64) > 0) yield return DayOfWeek.Sunday;
}
1
geofftnz

私が使用するここで私が言及したカップルがあります:

1
Keith

私はどこでもこれをしなければならないのが嫌いです:

DataSet ds = dataLayer.GetSomeData(1, 2, 3);
if(ds != null){
    if(ds.Tables.Count > 0){
        DataTable dt = ds.Tables[0];
        foreach(DataRow dr in dt.Rows){
            //Do some processing
        }
    }
}

代わりに、私は通常、次の拡張メソッドを使用します。

public static IEnumerable<DataRow> DataRows(this DataSet current){
    if(current != null){
        if(current.Tables.Count > 0){
            DataTable dt = current.Tables[0];
            foreach(DataRow dr in dt.Rows){
                yield return dr;
            }
        }
    }
}

したがって、最初の例は次のようになります。

foreach(DataRow row in ds.DataRows()){
    //Do some processing
}

イェーイ、拡張メソッド!

1
Mark Carpenter

これは、最初に単一の要素が追加された配列を作成します。

public static T[] Prepend<T>(this T[] array, T item)
{
    T[] result = new T[array.Length + 1];
    result[0] = item;
    Array.Copy(array, 0, result, 1, array.Length);
    return result;
}

string[] some = new string[] { "foo", "bar" };
...
some = some.Prepend("baz"); 

そして、これは、ある式を正方形に変換する必要があるときに役立ちます。

public static double Sq(this double arg)
{
    return arg * arg;
}

(x - x0).Sq() + (y - y0).Sq() + (z - z0).Sq()
1
okutane

これが私が書いたもう一つのものです:

    public static class StringExtensions
    {
        /// <summary>
        /// Returns a Subset string starting at the specified start index and ending and the specified end
        /// index.
        /// </summary>
        /// <param name="s">The string to retrieve the subset from.</param>
        /// <param name="startIndex">The specified start index for the subset.</param>
        /// <param name="endIndex">The specified end index for the subset.</param>
        /// <returns>A Subset string starting at the specified start index and ending and the specified end
        /// index.</returns>
        public static string Subsetstring(this string s, int startIndex, int endIndex)
        {
            if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", "Must be positive.");
            if (endIndex < 0) throw new ArgumentOutOfRangeException("endIndex", "Must be positive.");
            if (startIndex > endIndex) throw new ArgumentOutOfRangeException("endIndex", "Must be >= startIndex.");
            return s.Substring(startIndex, (endIndex - startIndex));
        }

        /// <summary>
        /// Finds the specified Start Text and the End Text in this string instance, and returns a string
        /// containing all the text starting from startText, to the begining of endText. (endText is not
        /// included.)
        /// </summary>
        /// <param name="s">The string to retrieve the subset from.</param>
        /// <param name="startText">The Start Text to begin the Subset from.</param>
        /// <param name="endText">The End Text to where the Subset goes to.</param>
        /// <param name="ignoreCase">Whether or not to ignore case when comparing startText/endText to the string.</param>
        /// <returns>A string containing all the text starting from startText, to the begining of endText.</returns>
        public static string Subsetstring(this string s, string startText, string endText, bool ignoreCase)
        {
            if (string.IsNullOrEmpty(startText)) throw new ArgumentNullException("startText", "Must be filled.");
            if (string.IsNullOrEmpty(endText)) throw new ArgumentNullException("endText", "Must be filled.");
            string temp = s;
            if (ignoreCase)
            {
                temp = s.ToUpperInvariant();
                startText = startText.ToUpperInvariant();
                endText = endText.ToUpperInvariant();
            }
            int start = temp.IndexOf(startText);
            int end = temp.IndexOf(endText, start);
            return Subsetstring(s, start, end);
        }
    }

これの背後にある動機は単純でした。組み込みのSubstringメソッドがstartindexとlengthをパラメーターとしてどのように取るかはいつも私を悩ませていました。 startindexとendindexを実行する方が常にはるかに役立ちます。だから、私は自分自身を転がしました:

使用法:

        string s = "This is a tester for my cool extension method!!";
        s = s.Subsetstring("tester", "cool",true);

Subsetstringを使用しなければならなかった理由は、Substringのオーバーロードがすでに2つのintを使用しているためです。誰かもっといい名前があれば教えてください!!

1
BFree

文字列utilsの私自身の個人的なコレクションからの私のお気に入りは、TryParseメソッドを持つ任意の型の文字列から強く型付けされた値を解析するものです。

public static class StringUtils
{
    /// <summary>
    /// This method will parse a value from a string.
    /// If the string is null or not the right format to parse a valid value,
    /// it will return the default value provided.
    /// </summary>
    public static T To<t>(this string value, T defaultValue)
        where T: struct
    {
        var type = typeof(T);
        if (value != null)
        {
            var parse = type.GetMethod("TryParse", new Type[] { typeof(string), type.MakeByRefType() });
            var parameters = new object[] { value, default(T) };
            if((bool)parse.Invoke(null, parameters))
                return (T)parameters[1];
        }
        return defaultValue;
    }

    /// <summary>
    /// This method will parse a value from a string.
    /// If the string is null or not the right format to parse a valid value,
    /// it will return the default value for the type.
    /// </summary>
    public static T To<t>(this string value)
        where T : struct
    {
        return value.To<t>(default(T));
    }
}

クエリ文字列から強く型付けされた情報を取得するのに最適です。

var value = Request.QueryString["value"].To<int>();
1
Helephant

String.formatは静的であってはなりません。だから私はfrmtと呼ばれる拡張メソッドを使用します:

<Extension()> Public Function frmt(ByVal format As String,
                                   ByVal ParamArray args() As Object) As String
    If format Is Nothing Then Throw New ArgumentNullException("format")
    Return String.Format(format, args)
End Function

バイナリライターを作成せずにバイトストリームに数値を読み書きしたい場合(技術的には、ライターでラップした後に生のストリームを変更することは想定されていません):

<Extension()> Public Function Bytes(ByVal n As ULong,
                                    ByVal byteOrder As ByteOrder,
                                    Optional ByVal size As Integer = 8) As Byte()
    Dim data As New List(Of Byte)
    Do Until data.Count >= size
        data.Add(CByte(n And CULng(&HFF)))
        n >>= 8
    Loop
    Select Case byteOrder
        Case ByteOrder.BigEndian
            Return data.ToArray.reversed
        Case ByteOrder.LittleEndian
            Return data.ToArray
        Case Else
            Throw New ArgumentException("Unrecognized byte order.")
    End Select
End Function
<Extension()> Public Function ToULong(ByVal data As IEnumerable(Of Byte),
                                      ByVal byteOrder As ByteOrder) As ULong
    If data Is Nothing Then Throw New ArgumentNullException("data")
    Dim val As ULong
    Select Case byteOrder
        Case ByteOrder.LittleEndian
            data = data.Reverse
        Case ByteOrder.BigEndian
            'no change required
        Case Else
            Throw New ArgumentException("Unrecognized byte order.")
    End Select
    For Each b In data
        val <<= 8
        val = val Or b
    Next b
    Return val
End Function
0
Craig Gidney
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string guid1 = "936DA01F-9ABD-4d9d-80C7-02AF85C822A8";
            string guid2 = "936DA01F-9ABD-4d9d-80C7-02AF85C822A";
            Console.WriteLine("guid1: {0}", guid1.IsGuid());
            Console.WriteLine("guid2: {0}", guid2.IsGuid());
            Console.ReadLine();
        }
    }

    public static class GuidUtility
    {
        /// <summary>
        /// Determines if string is legitimate GUID
        /// </summary>       
        public static Boolean IsGuid(this String s)
        {
            string pattern = @"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$";
            Regex regex = new Regex(pattern);
            return regex.IsMatch(s);
        }
    }
}
0
John Grove

マルチスレッドのWPFアプリケーション(ソケットやタイマーを使用している場合など)では、WPF要素の属性を変更するためにGUIスレッドを呼び出さなければならないことがよくあります。これは醜い肥大化したコードであり、特にすべてのメソッドで実行する必要があるためです。そのため、この拡張メソッドを作成しました。

    /// <summary>
    /// Invoke the element's thread using a dispatcher. This is needed for changing WPF element attributes.
    /// </summary>
    /// <param name="dispatcherObject">The element of which to use the thread.</param>
    /// <param name="action">The action to do with the invoked thread.</param>
    /// <param name="dispatcherPriority">The priority of this action.</param>
    public static void DoInvoked(this System.Windows.Threading.DispatcherObject dispatcherObject, Action action, System.Windows.Threading.DispatcherPriority dispatcherPriority = System.Windows.Threading.DispatcherPriority.Render)
    {
        if (System.Threading.Thread.CurrentThread == dispatcherObject.Dispatcher.Thread)
        {
            action();
        }
        else
        {
            dispatcherObject.Dispatcher.BeginInvoke(action, dispatcherPriority, null);
        }
    }

実装:

public partial class MainWindow : Window
{
    ... other code ...
    void updateTime(object sender, ElapsedEventArgs e)
    {
        this.DoInvoked(() => textBoxStatus.Text = "Done.");
    }
}
0
user287529

これは、指定されたアイテムを最初に取得するようにシーケンスをシフトします。たとえば、曜日を取得して、シーケンスの最初の日が現在のカルチャの最初の日になるようにシフトするために使用しました。

    /// <summary>
    /// Shifts a sequence so that the given <paramref name="item"/> becomes the first. 
    /// Uses the specified equality <paramref name="comparer"/> to find the item.
    /// </summary>
    /// <typeparam name="TSource">Type of elements in <paramref name="source"/>.</typeparam>
    /// <param name="source">Sequence of elements.</param>
    /// <param name="item">Item which will become the first.</param>
    /// <param name="comparer">Used to find the first item.</param>
    /// <returns>A shifted sequence. For example Shift({1,2,3,4,5,6}, 3) would become {3,4,5,6,1,2}. </returns>
    public static IEnumerable<TSource> Shift<TSource>(this IEnumerable<TSource> source, TSource item, IEqualityComparer<TSource> comparer)
    {
        var queue = new Queue<TSource>();
        bool found = false;

        foreach (TSource e in source)
        {
            if (!found && comparer.Equals(item, e))
                found = true;

            if (found)
                yield return e;
            else
                queue.Enqueue(e);
        }

        while (queue.Count > 0)
            yield return queue.Dequeue();
    }


    /// <summary>
    /// Shifts a sequence so that the given item becomes the first. 
    /// Uses the default equality comparer to find the item.
    /// </summary>
    /// <typeparam name="TSource">Type of elements in <paramref name="source"/>.</typeparam>
    /// <param name="source">Sequence of elements.</param>
    /// <param name="element">Element which will become the first.</param>
    /// <returns>A shifted sequence. For example Shift({1,2,3,4,5,6}, 3) would become {3,4,5,6,1,2}. </returns>
    public static IEnumerable<TSource> Shift<TSource>(this IEnumerable<TSource> source, TSource element)
    {
        return Shift(source, element, EqualityComparer<TSource>.Default);
    }
0
Svish

私はこれらの2つを書きませんでしたが、私は書きたかったのですが。 http://lostechies.com/jimmybogard/2009/10/16/more-missing-linq-operators/ で見つかりました

追加

public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element)
{
    using (IEnumerator<TSource> e1 = source.GetEnumerator())
        while (e1.MoveNext())
            yield return e1.Current;

    yield return element;
}

Prepend

public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource element)
{
    yield return element;

    using (IEnumerator<TSource> e1 = source.GetEnumerator())
        while (e1.MoveNext())
            yield return e1.Current;
}
0
Kane

これらのいくつかはすでに投稿されていますが、私はこれらのスレッドをいくつか見たことがあり、投票は有用なものの現実とは決して一致しないと言いたかっただけです。 IMO、これは本当に最も有用な拡張メソッドのリストです

someCollection.ForEach(i => i.DoSomething());

これは、組み込みのforeachステートメントに置き換わるものであり、それが使用される頻度は誰もが知っているため、非常に便利です。

7.CreateSequence();

これにより、0から6までのシーケンスが作成されます。開始点やステップの指定など、他のバージョンもあります。これは、forループを置き換えるため、2番目に便利な関数です。これはEnumerable.Range関数を複製すると言う人もいますが、これは本当ですが、linqで気に入っていることの1つは左から右の順序なので、次のようなことができます。

myCollection.Where(i => i.Something == somethingElse).Count().CreateSequence(). do something else

次に便利なのはCastToとAsです。ここでも、組み込みの機能を複製しますが、左から右の順序を保持します。 CastToは、単一のオブジェクトで機能するという点でCastとは異なることに注意してください。

myObject.CastTo<Person>().DoSomething()
myObject.As<Person>()

次に、SplitAsEnumerableがあります。これはsplitと同じように機能しますが、すべてを一度にメモリにロードするわけではありません。これは、大きなファイルの解析に最適です。文字列またはストリームで機能します。

myFileStream.SplitAsEnumerable("\r\n").Select(line => line.SplitAsEnumerable(","))

最後の1つは、コレクションを文字列に変換する方法です。これは、画面に内容を表示したり、ファイルに書き込んだりするのに最適です。例えば:

myTextBox.Text = "Top 3 scorers are " + myCollection.OrderBy(i => i.Score).Take(3).FlattenToString(i => i.Score.ToString(), ", ");
0
MikeKulls

これは私が最近仕事で書き、ブログに書いたものです:

http://crazorsharp.blogspot.com/2009/03/cool-ienumberable-extension-method_25.html

基本的にはIEnumerable.ToHtmlTable();です。

0
BFree

私はおそらくそれらを制約付きタイプに最もよく使用します。

何かのようなもの:

public class Gallons
{
    private int m_gallons;

    public Gallons(int count)
    {
        if(count < 0)
            throw new ArgumentException("Cannot have negative gallons");
        m_gallons = count;
    }

    static public Gallons operator + (Gallons left, Gallons right)
    {
        return new Gallons(left.m_gallons + right.m_gallons);
    }
    public override string ToString()
    {
        return m_gallons.ToString();
    }
}
public class Feet
{
    private int m_feet;

    public Feet(int count)
    {
        if(count < 0)
            throw new ArgumentException("Cannot have negative feet");
        m_feet = count;
    }

    static public Feet operator +(Feet left, Feet right)
    {
        return new Feet(left.m_feet + right.m_feet);
    }
    public override string ToString()
    {
        return m_feet.ToString();
    }
}

public static class Conversions
{
    static public Feet Feet(this int i)
    {
        return new Feet(i);
    }
    static public Gallons Gallons(this int i)
    {
        return new Gallons(i);
    }
}

public class Test
{
    static public int Main(string[] args)
    {
        System.Console.WriteLine(2.Feet() + 3.Feet()); // 5
        System.Console.WriteLine(3.Gallons() + 4.Gallons()); // 7
        System.Console.WriteLine(2.Feet() + 3.Gallons()); // doesn't compile - can't add feet to gallons!
        return 0;
    }
}
0
kyoryu

指定されたfileinfoクラスの指定されたプロパティを別のプロパティと比較します。

    public static bool compare(this FileInfo F1,FileInfo F2,string propertyName)
    {
        try
        {
            System.Reflection.PropertyInfo p1 = F1.GetType().GetProperty(propertyName);
            System.Reflection.PropertyInfo p2 = F2.GetType().GetProperty(propertyName);

                if (p1.GetValue(F1, null) == p2.GetValue(F1, null))
                {
                    return true;
                }

        }
        catch (Exception ex)
        {
            return false;
        }

        return false;
    }
0
RameshVel

Entity Frameworkで作業し、これに似たコード行を何度も使用したことがありますか?

_context.EmployeeSet.Include("Department")
    .Include("Manager").Include( ... ).Select();

これを行うのは簡単ではありませんか:

_context.EmployeeSet.IncludeCommonReferenceses().Select();

`

internal static class ObjectContextExtensions
{
    internal static ObjectQuery<Employee> IncludeCommonReferenceses(this ObjectQuery<Employee> employeeSet)
    {
        return employeeSet.Include(GetPropertyName<Employee>(e => e.Department))
           .Include(GetPropertyName<Employee>(e => e.Manager)).Include( ... );
    }

    private static string GetPropertyName<T>(Expression<Func<T, object>> subSelector)
    {
        return ((MemberExpression)subSelector.Body).Member.Name;
    }
}

Agasinでのリフレクションの使用を避けるために、プロパティ名をconstsに保存することをお勧めします。

0
HuBeZa

MVCプロジェクトのclass-name-to-upper-case-urlの問題を解決するために、以下を自動操縦します。

public static class RouteCollectionExt
{
    public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults)
    {
        var route = new LowercaseRoute(url, new RouteValueDictionary(defaults), new MvcRouteHandler());

        routes.Add(name, route);

        return route;
    }

    private class LowercaseRoute : Route
    {
        public LowercaseRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler) { }

        public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler) { }

        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler) { }

        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            : base(url, defaults, constraints, dataTokens, routeHandler) { }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            var path = base.GetVirtualPath(requestContext, values);

            if (path != null)
            {
                path.VirtualPath = path.VirtualPath.ToLowerInvariant();
            }

            return path;
        }
    }  
}

使用法:

routes.MapRouteLowercase(
  "Default",
  "{controller}/{action}/{id}",
  new { controller = "Home", action = "Index", id = "" } 
);
0
annakata

これは、ストリームから別のストリームにコピーするために、特にMemoryStreamにコピーするためによく使用するものです。

public static void CopyStream(this Stream destination, Stream source)
    {
        if (source.CanSeek)
        {
            source.Position = 0;
        }
        int Length = 64000;
        Byte[] buffer = new Byte[Length];
        int bytesRead = source.Read(buffer, 0, Length);
        // write the required bytes
        while (bytesRead > 0)
        {
            destination.Write(buffer, 0, bytesRead);
            bytesRead = source.Read(buffer, 0, Length);
        }
    }

実装:

MemoryStream result = new MemoryStream();
Stream s = new FileStream(tempDocFile, FileMode.Open);

result.CopyStream(s);

s.Close();
0
Jens