Какие ваши любимые методы расширения для C#? (codeplex.com/extensionoverflow)

Составим список ответов, где вы разместите свой отличный и любимый методы расширения.

Требование состоит в том, что должен быть опубликован полный код, а также пример и объяснение того, как его использовать.

Основываясь на большом интересе к этой теме, я установил проект с открытым исходным кодом под названием extensionoverflow на Codeplex.

Пожалуйста, отметьте свои ответы согласием на размещение кода в проекте Codeplex.

Пожалуйста, разместите полный исходный код, а не ссылку.

Новости Codeplex:

24.08.2010 Страница Codeplex теперь находится здесь: http://extensionoverflow.codeplex.com/

11.11.2008 XmlSerialize / XmlDeserialize теперь Реализовано и Единица протестирована.

11.11.2008 Есть еще место для новых разработчиков. ;-) Присоединяйся сейчас!

11.11.2008 Третий участник присоединился к ExtensionOverflow, добро пожаловать в Б.Кристенсен

11.11.2008 Формат: с теперь Реализовано и Единица протестирована.

09.11.2008 Второй участник присоединился к ExtensionOverflow. добро пожаловать в чакрит.

09.11.2008 Нам нужно больше разработчиков. ;-)

09.11.2008 ThrowIfArgumentIsNull теперь в Реализовано и Единица протестирована на Codeplex.

Теперь первый код зафиксирован на сайте Codeplex.

bovium 07.11.2008 23:13

Эрик, к сожалению, сейчас все запущено на codeplex. Пожалуйста, присоединяйтесь в любом случае.

bovium 12.11.2008 01:43

Выглядит неплохо. У меня есть комментарий по поводу именования статических классов. Называть их <type> Extensions не очень информативно. Например, StringExtensions содержит как форматирование, так и xml-файлы. Я думаю, что лучше назвать класс, почему вы расширяете этот тип. Например, UnixDateTimeConversions. Вы можете разумно предположить, что он содержит методы для преобразования времени Unix и обратно. Просто мысль!

ecoffey 26.08.2010 19:56

Проверьте этот URL-адрес, чтобы узнать больше о методах расширения C# planetofcoders.com/c-extension-methods

Gaurav Agrawal 09.03.2012 09:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
478
4
128 618
150

Ответы 150

ThrowIfArgumentIsNull - хороший способ выполнить ту нулевую проверку, которую мы все должны сделать.

public static class Extensions
{
    public static void ThrowIfArgumentIsNull<T>(this T obj, string parameterName) where T : class
    {
        if (obj == null) throw new ArgumentNullException(parameterName + " not allowed to be null");
    }
}

Ниже приведен способ его использования, и он работает со всеми классами в вашем пространстве имен или везде, где вы используете пространство имен внутри него.

internal class Test
{
    public Test(string input1)
    {
        input1.ThrowIfArgumentIsNull("input1");
    }
}

Можно использовать этот код в проекте CodePlex.

Мне это тоже нравится, у Джона это есть, и я использую что-то подобное от Umbrella, я могу отказаться от части «ArgumentIs».

cfeduke 07.11.2008 10:47

Да уж! это тоже метод расширения kewl :)

Pure.Krome 07.11.2008 12:55

Если вы используете конструктор ArgumentNullException только с 1 строковым аргументом, этот аргумент должен быть просто именем параметра, а не сообщением об ошибке. Итак, ваш код должен выглядеть так: if (obj == null) throw new ArgumentNullException (parameterName);

Tommy Carlier 27.11.2009 09:55

Я бы использовал для этого default(T) и удалил требование класса.

Joel Coehoorn 06.05.2010 17:07

@Joel: Значения, отличные от значений по умолчанию для собственных типов, чаще являются допустимыми аргументами, чем значения NULL. Для меня проверка на null имеет больше смысла, чем проверка на значение по умолчанию. Конечно, я просто обобщаю всю идею, говоря Require.ThatArgument(input != null) или Require.ThatArgument(personId > 0). Для этого не нужно так много кода, он намного более гибкий и хорошо читается. У меня есть дополнительные переопределения, которые используют функции, когда вы хотите настроить сообщение об ошибке или само исключение.

StriplingWarrior 26.08.2010 19:42

Чтобы включить имена и типы значений, допускающие значение NULL, я делаю это так: public static void ThrowIfNull <T> (этот элемент T, имя строки), где T: class {if (item == null) throw new ArgumentNullException (name);} public static void ThrowIfNull <T> (этот элемент T), где T: class {if (item == null) throw new ArgumentNullException ();} public static void ThrowIfNull <T> (этот элемент T?, имя строки), где T: struct {if (item == null) throw new ArgumentNullException (name);} public static void ThrowIfNull <T> (this T? item) где T: struct {if (item == null) throw new ArgumentNullException ();}

Wolf5370 02.05.2012 11:40

строка.Формат ярлыка:

public static class StringExtensions
{
    // Enable quick and more natural string.Format calls
    public static string F(this string s, params object[] args)
    {
        return string.Format(s, args);
    }
}

Пример:

var s = "The co-ordinate is ({0}, {1})".F(point.X, point.Y);

Для быстрого копирования и вставки перейдите здесь.

Не кажется ли вам более естественным набирать "some string".F("param") вместо string.Format("some string", "param")?

Чтобы получить другое имя удобочитаемый, попробуйте одно из следующих предложений:

s = "Hello {0} world {1}!".Fmt("Stack", "Overflow");
s = "Hello {0} world {1}!".FormatBy("Stack", "Overflow");
s = "Hello {0} world {1}!".FormatWith("Stack", "Overflow");
s = "Hello {0} world {1}!".Display("Stack", "Overflow");
s = "Hello {0} world {1}!".With("Stack", "Overflow");

..

Это, конечно, коротко, но будет нечитаемо для новых членов вашей команды.

Jon Skeet 07.11.2008 10:15

удобочитаемость - единственная причина, по которой я бы не стал ее использовать, кроме того, это хорошая идея

Nathan W 07.11.2008 10:20

Я бы назвал его FormatWith или что-то в этом роде.

mmiika 07.11.2008 10:20

Я думаю, что удобочитаемость имеет большее значение в более крупной схеме вашего кода, чем несколько сокращенных операторов, которые можно быстро найти / спросить.

chakrit 07.11.2008 10:23

Лично мне нужен отдельный объект Formatter, который BCL мог бы анализировать один раз и повторно использовать. Это повысит читаемость и производительность. Я попросил команду BCL - посмотрим ...

Jon Skeet 07.11.2008 10:25

Разве это не было сделано? Поскольку это просто еще один способ вызвать в BCL?

chakrit 07.11.2008 10:55

chakrit: Нет - потому что каждый раз, когда вы форматируете, BCL будет анализировать строку формата. Строки формата обычно остаются неизменными на протяжении всего жизненного цикла программы - зачем их анализировать более одного раза каждую?

Jon Skeet 07.11.2008 10:58

Что это дает, кроме добавления еще одного вызова метода ... Для меня это вообще не выглядит читабельным.

Vyas Bharghava 07.11.2008 12:11

@Jon_Skeet Понятно. Это добавило бы больше ценности методу, хе-хе.

chakrit 07.11.2008 12:58

Я не понимаю ... Если вы раньше использовали string.format, то наверняка быстро догадались бы, что это вызов string.format. И быстрое определение goto скажет вам об этом. Если вам это не нравится, просто назовите его «Формат» или как хотите. Взгляните на оператор формата "%" в Python.

chakrit 07.11.2008 13:00

Вам следует добавить несколько перегрузок, которые принимают фиксированное количество параметров. Использование параметров происходит медленно!

Rune Grimstad 07.11.2008 13:16

@Rune Я сделал, я просто привел это в качестве примера. У меня тоже есть собственная версия библиотеки расширений утилит.

chakrit 07.11.2008 13:33

Вы не можете называть это «форматом», иначе он не будет компилироваться. Я называю его "Inject", когда использую подобное расширение.

Damir Zekić 08.11.2008 12:04

Я бы добавил немного CultureInfo.CurrentCulture и создал другую версию CultureInfo.InvariantCulture.

hangy 08.11.2008 15:28

Переименуйте "F" в "FormatWith" и будет лучше. Функционал хороший, но название нужно поправить.

Timothy Khouri 09.11.2008 01:15

Console.WriteLine уже имеет перегрузку, которая форматирует. Console.WriteLine ("{0} {1}", значение1, значение2)

Stefan Schultze 01.12.2008 02:34

Мне нравится название "FormatBy" ... оно немного короче, чем "FormatWith"

Alex Baranosky 15.01.2009 02:09

Это метод расширения курс, он будет нечитаемым для новых членов команды. Я думал, что это была идея с этой остроумной штукой? Как еще новые участники узнают, насколько мы умны?

MarkJ 28.02.2009 12:13

Хорошо ... Я просто пошел, чтобы привести это в действие, и выбрал .With - так что вы получите "This is a {0}". With ("test"), и это очень удобно для чтения и имеет смысл. К вашему сведению

klkitchens 04.03.2009 16:49

Я присоединился к новой команде около 5 месяцев назад и вижу, что этот глупый метод ".F" используется очень часто. И, конечно же, они отказываются менять название ...

kirk.burleson 15.08.2011 05:38

gitorious.org/cadenza - это полная библиотека некоторых из наиболее полезных методов расширения, которые я видел.

12 довольно простых методов расширения. Меня немного не впечатляют моно-камни.

mackenir 07.11.2008 13:07

(Я говорю о выпущенной версии, а не о той, которую вам нужно получить с помощью системы управления версиями)

mackenir 07.11.2008 13:27

Преобразуйте double в строку, отформатированную с использованием указанного языка и региональных параметров:

public static class ExtensionMethods 
{
  public static string ToCurrency(this double value, string cultureName)
  {
    CultureInfo currentCulture = new CultureInfo(cultureName);
    return (string.Format(currentCulture, "{0:C}", value));
  }
}

Пример:

double test = 154.20;
string testString = test.ToCurrency("en-US"); // $154.20

Вы должны использовать десятичное значение для валюты, иначе у вас возникнут проблемы с округлением

Andrew Bullock 07.11.2008 13:00

Как насчет использования Enum в параметре вместо простой строки

Rulas 22.01.2009 19:02
public static class StringExtensions {

    /// <summary>
    /// Parses a string into an Enum
    /// </summary>
    /// <typeparam name = "T">The type of the Enum</typeparam>
    /// <param name = "value">String value to parse</param>
    /// <returns>The Enum corresponding to the stringExtensions</returns>
    public static T EnumParse<T>(this string value) {
        return StringExtensions.EnumParse<T>(value, false);
    }

    public static T EnumParse<T>(this string value, bool ignorecase) {

        if (value == null) {
            throw new ArgumentNullException("value");
        }

        value = value.Trim();

        if (value.Length == 0) {
            throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
        }

        Type t = typeof(T);

        if (!t.IsEnum) {
            throw new ArgumentException("Type provided must be an Enum.", "T");
        }

        return (T)Enum.Parse(t, value, ignorecase);
    }
}

Полезно для синтаксического анализа строки в Enum.

public enum TestEnum
{
    Bar,
    Test
}

public class Test
{
    public void Test()
    {
        TestEnum foo = "Test".EnumParse<TestEnum>();
    }
 }

Кредит идет на Скотт Дорман

--- Редактировать для проекта Codeplex ---

Я спросил Скотта Дормана, не возражает ли он, чтобы мы опубликовали его код в проекте Codeplex. Это ответ, который я получил от него:

Thanks for the heads-up on both the SO post and the CodePlex project. I have upvoted your answer on the question. Yes, the code is effectively in the public domain currently under the CodeProject Open License (http://www.codeproject.com/info/cpol10.aspx).

I have no problems with this being included in the CodePlex project, and if you want to add me to the project (username is sdorman) I will add that method plus some additional enum helper methods.

Этот сценарий анализа перечисления возникает постоянно ... нужно поместить это в мою библиотеку :-)

chakrit 09.11.2008 18:33

Вау, я писал методы для сопоставления строк с перечислениями (только начал использовать .NET). Спасибо, это абсолютно поможет!

Kevin 13.11.2008 04:29

Вы также можете назвать это ToEnum <> (), поскольку оно идет после объекта.

Neil 14.11.2008 07:57

Обратите внимание, что Enum.TryParse <T> был добавлен в Net 4.0 - blogs.msdn.com/bclteam

Dan Diplo 16.11.2009 17:47

Я не думаю, что этот метод должен использовать Trim. Обрезка ввода должна быть ответственностью вызывающей стороны.

CodesInChaos 14.10.2010 15:02

Обязательно поместите это в проект codeplex.

Сериализация / десериализация объектов в XML:

/// <summary>Serializes an object of type T in to an xml string</summary>
/// <typeparam name = "T">Any class type</typeparam>
/// <param name = "obj">Object to serialize</param>
/// <returns>A string that represents Xml, empty otherwise</returns>
public static string XmlSerialize<T>(this T obj) where T : class, new()
{
    if (obj == null) throw new ArgumentNullException("obj");

    var serializer = new XmlSerializer(typeof(T));
    using (var writer = new StringWriter())
    {
        serializer.Serialize(writer, obj);
        return writer.ToString();
    }
}

/// <summary>Deserializes an xml string in to an object of Type T</summary>
/// <typeparam name = "T">Any class type</typeparam>
/// <param name = "xml">Xml as string to deserialize from</param>
/// <returns>A new object of type T is successful, null if failed</returns>
public static T XmlDeserialize<T>(this string xml) where T : class, new()
{
    if (xml == null) throw new ArgumentNullException("xml");

    var serializer = new XmlSerializer(typeof(T));
    using (var reader = new StringReader(xml))
    {
        try { return (T)serializer.Deserialize(reader); }
        catch { return null; } // Could not be deserialized to this type.
    }
}

Я обнаружил, что пишу что-то очень похожее.

cfeduke 07.11.2008 10:45

А что с сумасшедшим действием подстроки? Если вы пытаетесь избавиться от спецификации, есть гораздо лучшие способы сделать это (например, указать, что она не включена в кодировку).

Greg Beech 01.12.2008 03:39

Я бы хотел назвать первый ToXml() (например, ToString())

Jay Bazuzi 06.01.2009 23:29

Ооо, у вас там будет неприятная утечка памяти. Убедитесь, что у вас есть личный Dictionary <Type, XmlSerializer> и кэшируйте их там. Причина этого в том, что каждый раз, когда вы создаете сериализатор .Net загружает новую сборку в ваш домен приложения.

Jonathan C Dickinson 08.01.2009 10:28

Приносим извинения OP, если он намеренно написал это таким образом, но использование MemoryStreams И XmlReader / XmlWriter было излишним. Классы StringReader и StringWriter идеально подходят для этой операции.

Portman 09.01.2009 00:47

Не проблема !, я сам воспользуюсь этой измененной версией, спасибо.

TWith2Sugars 20.01.2009 20:10

Просто подумайте ... стоит ли использовать ключевое слово as и просто вернуть результат вместо преобразования и перехвата исключения?

TWith2Sugars 20.01.2009 20:14

@Jonathan C Dickinson Спасибо за предупреждение!

TWith2Sugars 22.01.2009 18:28

Остерегайтесь, это небезопасно. Вам обязательно нужно синхронизировать доступ к словарю статических сериализаторов.

Yann Schwartz 06.06.2009 00:48

@Yann Schwartz С чего бы мне начать?

TWith2Sugars 06.06.2009 03:00

@Yann, @T, намного проще, если просто добавить атрибут "thread static". Затем для каждого потока будет создан новый кеш. Нет необходимости в синхронизации.

Frank Krueger 15.11.2009 01:50

@Jonathan C Dickinson: из документов MSDN здесь msdn.microsoft.com/en-us/library/… видно, что используемый конструктор (новый XmlSerializer (тип)) не имеет проблемы с утечкой памяти. Так, может быть, код кеширования не нужен?

slolife 26.08.2010 21:27

@Jonathan, @Yann, @Frank @slolife, @ TWith2Sugars - После прочтения ссылки MSDN (msdn.microsoft.com/en-us/library/…), предоставленной @slolife, я думаю, можно с уверенностью сказать, что нам не нужно беспокоиться о создании новой сборки при каждом вызове (как было предложено @Jonathan). Я удалил лишнее кеширование и почистил код.

Greg 25.02.2011 09:57

DateTimeExtensions

Примеры:

DateTime firstDayOfMonth = DateTime.Now.First();
DateTime lastdayOfMonth = DateTime.Now.Last();
DateTime lastFridayInMonth = DateTime.Now.Last(DayOfWeek.Friday);
DateTime nextFriday = DateTime.Now.Next(DayOfWeek.Friday);
DateTime lunchTime = DateTime.Now.SetTime(11, 30);
DateTime noonOnFriday = DateTime.Now.Next(DayOfWeek.Friday).Noon();
DateTime secondMondayOfMonth = DateTime.Now.First(DayOfWeek.Monday).Next(DayOfWeek.Monday).Midnight();

Я бы предложил переименовать "SetTime" в "WithTime", поскольку он фактически не устанавливает его в существующем значении. Хотя в остальном неплохо.

Jon Skeet 07.11.2008 10:26

DateTime.Now.First () - что в первую очередь? Это видно только из примера кода.

mackenir 07.11.2008 13:08

Очень хорошо. Но согласитесь, названия могли бы быть намного лучше.

bovium 07.11.2008 20:46

DateTime.Now.First будет достаточно ясным в Intellisense, если метод хорошо документирован.

Ryan Lundy 11.12.2009 22:25

Легко сериализуйте объекты в XML:

public static string ToXml<T>(this T obj) where T : class
{
    XmlSerializer s = new XmlSerializer(obj.GetType());
    using (StringWriter writer = new StringWriter())
    {
        s.Serialize(writer, obj);
        return writer.ToString();
    }
}

"<root><child>foo</child</root>".ToXml<MyCustomType>();

также дубликат stackoverflow.com/questions/271398/…

Eric Schoonover 11.11.2008 11:24

Еще один полезный для меня:

/// <summary>
/// Converts any type in to an Int32
/// </summary>
/// <typeparam name = "T">Any Object</typeparam>
/// <param name = "value">Value to convert</param>
/// <returns>The integer, 0 if unsuccessful</returns>
public static int ToInt32<T>(this T value)
{
  int result;
  if (int.TryParse(value.ToString(), out result))
  {
    return result;
  }
  return 0;
}

/// <summary>
/// Converts any type in to an Int32 but if null then returns the default
/// </summary>
/// <param name = "value">Value to convert</param>
/// <typeparam name = "T">Any Object</typeparam>
/// <param name = "defaultValue">Default to use</param>
/// <returns>The defaultValue if unsuccessful</returns>
public static int ToInt32<T>(this T value, int defaultValue)
{
  int result;
  if (int.TryParse(value.ToString(), out result))
  {
    return result;
  }
  return defaultValue;
}

Пример:

int number = "123".ToInt32();

или же:

int badNumber = "a".ToInt32(100); // Returns 100 since a is nan

Да, тот же ToInt64, ToFloat и т. д. Вы можете исключить if и свести его к одному возврату, если хотите.

cfeduke 07.11.2008 10:49

ага, у меня тоже была куча таких. Но он был слишком большим, когда вам нужно было string.ToInt32 double.ToInt32 float.ToInt32 .. бла-бла Я думаю, что я немного злоупотребил этим :-)

chakrit 07.11.2008 13:37

Пабло Марамбио - какие альтернативные способы я мог бы выполнить?

TWith2Sugars 11.11.2008 10:15

Если я правильно помню, convert.ToInt32 мог вызвать исключение

TWith2Sugars 11.11.2008 11:19

У меня есть аналогичный метод Parse <T>, который я использую для всех типов, а не только для int32.

John 26.06.2010 17:01

У меня есть различные методы расширения в моем проекте MiscUtil (полный исходный код доступен там - я не собираюсь повторять его здесь). Мои любимые, некоторые из которых включают другие классы (например, диапазоны):

Дата и время - в основном для модульных тестов. Не уверен, что буду использовать их в продакшене :)

var birthday = 19.June(1976);
var workingDay = 7.Hours() + 30.Minutes();

Диапазоны и степпинг - огромное спасибо Марку Гравеллу за его операторский персонал, который сделал это возможным:

var evenNaturals = 2.To(int.MaxValue).Step(2);
var daysSinceBirth = birthday.To(DateTime.Today).Step(1.Days());

Сравнения:

var myComparer = ProjectionComparer.Create(Person p => p.Name);
var next = myComparer.ThenBy(p => p.Age);
var reversed = myComparer.Reverse();

Проверка аргумента:

x.ThrowIfNull("x");

LINQ to XML применяется к анонимным типам (или другим типам с соответствующими свойствами):

// <Name>Jon</Name><Age>32</Age>
new { Name = "Jon", Age=32}.ToXElements();
// Name = "Jon" Age = "32" (as XAttributes, obviously)
new { Name = "Jon", Age=32}.ToXAttributes()

Нажать LINQ - объяснение здесь заняло бы слишком много времени, но ищите его.

Это мило! Вы должны разместить его в Google Code или CodePlex, чтобы я мог выслать вам несколько патчей :-) Я обещаю, что он будет удобочитаемым :-P

chakrit 07.11.2008 10:30

Да, я когда-нибудь перенесу его в Google Code ... и, наверное, заодно переименую! (MiscUtil - ужасное имя.)

Jon Skeet 07.11.2008 10:45

@bovium: Вы уже видите код. Перейдите по ссылке в первом предложении - там есть полный исходный код.

Jon Skeet 07.11.2008 22:11

@ Джон Скит. Могу ли я использовать код и поместить его в проект Codeplex?

bovium 08.11.2008 21:28

@bovium: Я лучше сделаю это сам, выложу на code.google.com и сам буду управлять проектом, если вы не возражаете. Очевидно, что вы имеете право разместить его на Codeplex, если сохраните соответствующую атрибуцию, но я лучше разберусь с этим сам в ближайшее время, если вы не в отчаянии :)

Jon Skeet 08.11.2008 23:16

@ Джон Скит. Он размещен под лицензией MIT, и его можно использовать бесплатно для всех. Коммерчески или с открытым исходным кодом. Почему бы не объединить усилия и не создать публичную библиотеку методов расширения.

bovium 09.11.2008 00:47

Просто потому, что я делаю много других кусочков в этой библиотеке. Вы можете сделать копию всего этого для своего проекта, но я бы предпочел сохранить одну копию в моем собственном проекте.

Jon Skeet 09.11.2008 11:38

x.ThrowIfNull мне кажется неправильным. Вы вызываете метод того, что выглядит как член экземпляра, проверяя его значение null. Я знаю, что это сработает, поскольку методы расширения - это просто статические методы, это просто не интуитивно понятно.

JoshBerke 09.05.2009 19:06

@Josh: Сначала это неинтуитивно, но как только вы к этому привыкнете (что В самом деле не займет много времени), он будет кратким и очень читаемым. Держите свой разум открытым для новых способов делать что-то :)

Jon Skeet 09.05.2009 20:13

Джон, на вашем сайте сказано, что это "под лицензией Apache ...". Какая лицензия правильная: MIT / Apache? Спасибо

JP. 02.06.2010 21:26
19.June(1976) в порядке (по крайней мере, для тестирования), но мне не нравится 30.Minutes(). Почему бы не использовать перегрузку операторов, чтобы достичь чего-то более эквивалентного научным обозначениям, например 30 * min или 30 * Minutes, если хотите.
Lasse Espeholt 01.10.2010 01:06

@lasseespeholt: Ну, в обоих случаях вам нужно, чтобы min и Minutes были определены где-то в каждом исходном файле, который их использует ... и это может раздражать. В случае метода расширения это всего лишь случай добавления директивы using (на которую мы все равно не обращаем внимания). Но это определенно дело вкуса.

Jon Skeet 01.10.2010 01:15

@Jon Ahh хороший момент;) потому что он может быть статическим, поэтому вы можете сделать что-то вроде 30 * SI.min, но это просто сделает его менее понятным.

Lasse Espeholt 01.10.2010 01:18
public static class ComparableExtensions
{
  public static bool Between<T>(this T actual, T lower, T upper) where T : IComparable<T>
  {
    return actual.CompareTo(lower) >= 0 && actual.CompareTo(upper) < 0;
  }
}

Пример:

if (myNumber.Between(3,7))
{
  // ....
}

Это хорошо, я помню, как в моем последнем фреймворке 1.1 писал серию BetweenInt32, BetweenDateTime и т. д.

cfeduke 07.11.2008 11:07

Мне это нравится, но я пытаюсь решить, правильно ли делать проверку границ включительно для минимального значения, но исключая для максимального значения. Интересно, сбивает ли это с толку. 5.Between (5,10) - истина, а 5.Between (1,5) - ложь. Даже не уверен, что метод-компаньон Within поможет. Thougts?

Steve Hiner 07.11.2008 22:52

Разве название «IsBetween» не имело бы больше смысла? Также можно сделать IsBetweenInclusive и IsBetweenExclusive. Не знаю, какой из них выбрать по умолчанию.

fretje 04.06.2009 13:06

@Steve: было бы больше смысла, если бы это было расширение datetime.

Joel Coehoorn 09.06.2009 21:06

Для меня между подразумевается: 5.Between (5,10) возвращает false, а 10.Between (5,10) также возвращает false. Мне это кажется естественным.

Alex Baranosky 16.08.2009 13:13

Мне кажется, что у разных людей разные представления о том, что естественно. Из-за этого, вероятно, следует явно указать, что используется (например, «Включающий» против «Исключающий»), поскольку это может быть очень простым источником ошибок.

David Miani 01.12.2009 06:55

И Python, и Common Lisp используют инклюзивный на нижнем уровне и эксклюзивный на верхнем, как в приведенном выше коде. Да, сначала это кажется «неестественным», но в первые 5 минут он становится чрезвычайно полезным - я бы классифицировал его как неправильный. В конце концов, каждый символ в приведенном выше коде (кроме, возможно, <) сначала тоже казался мне «неестественным» - писать public static class ComparableExtensions { до того, как мой код, не совсем интуитивно.

Ken 03.06.2010 19:39

Я сделал аналогичный метод, но я также добавил перегрузку, которая использует перечисление BetweenType, чтобы указать, как должно происходить сравнение. иногда вам нужно истинное между (исключительное), иногда включающее, а иногда включающее ту или иную границу.

John 26.06.2010 16:38

Delphi избегает путаницы с «между», вызывая свою функцию «InRange» и возвращая истину в конечных точках. Я считаю, что это более полезное имя, чем двусмысленное «между».

Joe White 05.07.2010 20:53

Я бы, вероятно, просто добавил перегрузку с третьим параметром, чтобы сделать его более читаемым, и предоставить сопутствующее перечисление Match.Inclusive, Match.Exclusive. По умолчанию третий параметр делает его включающим, но перегрузка позволяет явно указать это: 5.Between (1, 5); возвращает true, но 5.Between (1, 5, Match.Exclusive); возвращает false.

BenAlabaster 23.07.2010 23:17

Будьте осторожны при использовании поплавка или двойника. Их семантика различается между IComparer и операторами <=> для бесконечностей и NaN. То, что 0 подписан (есть разные +0 и -0), тоже может создать проблемы.

CodesInChaos 14.10.2010 14:56

По умолчанию должно быть включено. Если кто-то говорит вам: «Выберите число от 1 до 9», в большинстве ситуаций возможными ответами будут 1 и 9.

Espo 26.07.2011 15:58

Используйте enum (neither, lower, upper, both) в качестве третьего параметра, чтобы указать равенство границ. по умолчанию neither.

zzzzBov 13.02.2012 22:36

Более простой способ загрузить настройки по умолчанию из коллекции (в реальной жизни я использую его для заполнения настроек из любого источника, включая командную строку, параметры URL ClickOnce и т. д.):

public static void LoadFrom(this ApplicationSettingsBase settings, NameValueCollection configuration)
{
    if (configuration != null)
        foreach (string key in configuration.AllKeys)
            if (!String.IsNullOrEmpty(key))
                try
                {
                    settings[key] = configuration.Get(key);
                }
                catch (SettingsPropertyNotFoundException)
                {
                  // handle bad arguments as you wish
                }
}

Пример:

Settings.Default.LoadFrom(new NameValueCollection() { { "Setting1", "Value1" }, { "Setting2", "Value2" } });

Хм, а что именно с этим было не так? Я не возражаю против отрицательного голоса, но я, честно говоря, хотел бы знать, должно ли оно работать или что-то в этом роде.

Alan 07.11.2008 23:14

Полезно для модульного тестирования:

public static IList<T> Clone<T>(this IList<T> list) where T : ICloneable
{
    var ret = new List<T>(list.Count);
    foreach (var item in list)
        ret.Add((T)item.Clone());

    // done
    return ret;
}

Серия таких как TWith2Sugars, альтернативный сокращенный синтаксис:

public static long? ToNullableInt64(this string val)
{
    long ret;
    return Int64.TryParse(val, out ret) ? ret : new long?();
}

И, наконец, это - есть ли что-то в BCL, что делает следующее?

public static void Split<T>(this T[] array, 
    Func<T,bool> determinator, 
    IList<T> onTrue, 
    IList<T> onFalse)
{
    if (onTrue == null)
        onTrue = new List<T>();
    else
        onTrue.Clear();

    if (onFalse == null)
        onFalse = new List<T>();
    else
        onFalse.Clear();

    if (determinator == null)
        return;

    foreach (var item in array)
    {
        if (determinator(item))
            onTrue.Add(item);
        else
            onFalse.Add(item);
    }
}

Вы можете сделать это с помощью двух операторов linq select-where. Считается ли это BCL?

chakrit 07.11.2008 13:39

да. (Обязательное ограничение на количество символов комментария здесь.)

cfeduke 07.11.2008 18:19

Мне нравятся эти расширения NUnit Assert: http://svn.caffeine-it.com/openrasta/trunk/src/Rasta.Testing/AssertExtensions.cs

Выскакивает с паролем. Просто нажмите "Отмена".

bovium 07.11.2008 12:33

Метод расширения:

public static void AddRange<T, S>(this ICollection<T> list, params S[] values)
    where S : T
{
    foreach (S value in values)
        list.Add(value);
}

Этот метод применяется ко всем типам и позволяет добавлять в список ряд элементов в качестве параметров.

Пример:

var list = new List<Int32>();
list.AddRange(5, 4, 8, 4, 2);

Лучше бы этот IList <T>

user1228 18.05.2009 16:32

Просто используйте инициализатор коллекции => var list = new List<int>{5,4,8,4,2};

Arnis Lapsa 02.11.2009 21:58

Почему бы просто не вызвать List <T> .AddRange (IEnumerable <T> collection) в вашем методе?

Rauhotz 13.03.2010 21:59

@Will: Фактически, было бы Лучший, чтобы принять ICollection<T>; тогда его можно было бы использовать, например, на LinkedList<T> и HashSet<T>, а не только на индексированных коллекциях.

Dan Tao 14.03.2010 00:15

@Arnis L это позволяет вам ДОБАВЛЯТЬ элементы в уже существующий List <T> и может вызываться несколько раз.

Aran Mulholland 30.03.2010 10:32

Я бы переименовал это в AppendRange() - это делает разницу между этим и инициализатором коллекции более очевидной.

Joel Coehoorn 06.05.2010 17:17

Почему вы зацикливаете значения, когда можете вызвать list.AddRange(values);?

stevehipwell 14.05.2010 13:35

Отредактировано как разрешить ковариацию в pre-.net 4.0

BlueRaja - Danny Pflughoeft 02.06.2010 19:42

Не компилируется, проблема с S и T.

KdgDev 12.07.2010 03:52

Мне не нужно добавлять кучу жестко закодированных значений в список почти так часто, как мне обычно нужно добавлять значения в IEnumerable. Когда мне нужно сделать что-то подобное, я просто говорю items = items.Concat(new[] {5, 4, 8, 4, 2}). Он работает с любым IEnumerable (как для субъекта, так и для аргумента), и если вы действительно хотите получить список, просто вызовите ToList(), когда закончите.

StriplingWarrior 26.08.2010 19:57

Обобщено на ICollection <T>

Greg 15.10.2010 20:13

HTH. Это одни из моих основных.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Insert.Your.Namespace.Here.Helpers
{
    public static class Extensions
    {
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> iEnumerable)
        {
            // Cheers to Joel Mueller for the bugfix. Was .Count(), now it's .Any()
            return iEnumerable == null ||
                   !iEnumerable.Any();
        }

        public static IList<T> ToListIfNotNullOrEmpty<T>(this IList<T> iList)
        {
            return iList.IsNullOrEmpty() ? null : iList;
        }

        public static PagedList<T> ToPagedListIfNotNullOrEmpty<T>(this PagedList<T> pagedList)
        {
            return pagedList.IsNullOrEmpty() ? null : pagedList;
        }

        public static string ToPluralString(this int value)
        {
            return value == 1 ? string.Empty : "s";
        }

        public static string ToReadableTime(this DateTime value)
        {
            TimeSpan span = DateTime.Now.Subtract(value);
            const string plural = "s";


            if (span.Days > 7)
            {
                return value.ToShortDateString();
            }

            switch (span.Days)
            {
                case 0:
                    switch (span.Hours)
                    {
                        case 0:
                            if (span.Minutes == 0)
                            {
                                return span.Seconds <= 0
                                           ? "now"
                                           : string.Format("{0} second{1} ago",
                                                           span.Seconds,
                                                           span.Seconds != 1 ? plural : string.Empty);
                            }
                            return string.Format("{0} minute{1} ago",
                                                 span.Minutes,
                                                 span.Minutes != 1 ? plural : string.Empty);
                        default:
                            return string.Format("{0} hour{1} ago",
                                                 span.Hours,
                                                 span.Hours != 1 ? plural : string.Empty);
                    }
                default:
                    return string.Format("{0} day{1} ago",
                                         span.Days,
                                         span.Days != 1 ? plural : string.Empty);
            }
        }

        public static string ToShortGuidString(this Guid value)
        {
            return Convert.ToBase64String(value.ToByteArray())
                .Replace("/", "_")
                .Replace("+", "-")
                .Substring(0, 22);
        }

        public static Guid FromShortGuidString(this string value)
        {
            return new Guid(Convert.FromBase64String(value.Replace("_", "/")
                                                         .Replace("-", "+") + "= = "));
        }

        public static string ToStringMaximumLength(this string value, int maximumLength)
        {
            return ToStringMaximumLength(value, maximumLength, "...");
        }

        public static string ToStringMaximumLength(this string value, int maximumLength, string postFixText)
        {
            if (string.IsNullOrEmpty(postFixText))
            {
                throw new ArgumentNullException("postFixText");
            }

            return value.Length > maximumLength
                       ? string.Format(CultureInfo.InvariantCulture,
                                       "{0}{1}",
                                       value.Substring(0, maximumLength - postFixText.Length),
                                       postFixText)
                       :
                           value;
        }

        public static string SlugDecode(this string value)
        {
            return value.Replace("_", " ");
        }

        public static string SlugEncode(this string value)
        {
            return value.Replace(" ", "_");
        }
    }
}

В случае IsNullOrEmpty я бы не хотел называть его перечислителем с миллионами элементов. Он будет перебирать все миллионы элементов, просто чтобы сказать мне, что он не пустой. Лучше: вернуть iEnumerable == null || ! iEnumerable.Any ();

Joel Mueller 08.11.2008 07:41

О, чувак - офигенный соус! я никогда этого не знал! ура кучи чувак. (мой пост выше, отредактированный.)

Pure.Krome 08.11.2008 08:36

Рад, что вам это нравится. Одно - Any () возвращает логическое значение, поэтому <= 0, вероятно, не будет компилироваться. Если перечисляемое пусто, Any () вернет false. Этот шрифт затрудняет просмотр, но в моем исходном примере перед вызовом Any был восклицательный знак.

Joel Mueller 08.11.2008 10:30

@ Pure.Krome Могу ли я использовать код в проекте codeplex. И вы не хотите стать участником этого проекта?

bovium 09.11.2008 00:58

ToPluralString () просто упрощен. Английский не является моим родным языком, поэтому мне это кажется глупым, но он даже не очень хорошо работает с английским языком в целом. ;-)

peSHIr 05.07.2010 17:27

@peSHIr: хотите привести пример? в основном, для большого количества (давайте будем упрощать и сказать «большинство») «вещей», которые либо 0, либо 2+ .. оканчиваются s, чтобы определить, что они являются множественным числом (или нет для 0). например. кошка / кошки. собака / собаки. друг / друзья. ошибка / ошибки. Теперь это не сработает для таких вещей, как фея / феи (и все другие слова, оканчивающиеся на y) или зуб / зубы ... и т.д. модификации единственного числа во множественное число.

Pure.Krome 06.07.2010 03:58

@ Pure.Krome: Вы уже сами приводите примеры. ;-) Еще: index / index, saldo / saldi и другие латинские. Если это просто помощник для использования в ToReadableTime () - где он будет отлично работать, даже если он никак не глобализован, но функция не используется (?) - я бы ожидал, что ToPluralString () будет закрытым вместо этого общественности.

peSHIr 06.07.2010 13:16

Это какая польза?

public static bool CoinToss(this Random rng)
{
    return rng.Next(2) == 0;
}

public static T OneOf<T>(this Random rng, params T[] things)
{
    return things[rng.Next(things.Length)];
}

Random rand;
bool luckyDay = rand.CoinToss();
string babyName = rand.OneOf("John", "George", "Radio XBR74 ROCKS!");

это имитирует функцию pythons random.choice (seq). красивый.

Daren Thomas 03.12.2009 17:04

Пара вещей: я бы рекомендовал, чтобы OneOf принимал ЛюбыеIList<T>. Тогда вы всегда можете иметь перегрузку также, которая принимает аргумент params и просто передает его в перегрузку IList<T>. Я дал ответ (прямо сейчас внизу) с помощью метода NextBool, аналогичного вашему CoinToss, но с перегрузкой, которая принимает параметр probability (что, если я хочу, чтобы что-то происходило в 75% случаев?). Кроме того, просто придирка: ваш пример кода выдаст NullReferenceException, поскольку rand никогда не инициализируется.

Dan Tao 14.03.2010 00:13

+1 Мне это очень нравится, но я предпочитаю, чтобы CoinToss был реализован с rng.NextDouble() < .5, потому что внутри .Next(int) сделан с .NextDouble(), поэтому вы должны сохранить приведение, * и чек.

Lasse Espeholt 01.10.2010 01:15

Вот тот, который я часто использую для форматирования презентаций.

public static string ToTitleCase(this string mText)
{
    if (mText == null) return mText;

    System.Globalization.CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
    System.Globalization.TextInfo textInfo = cultureInfo.TextInfo;

    // TextInfo.ToTitleCase only operates on the string if is all lower case, otherwise it returns the string unchanged.
    return textInfo.ToTitleCase(mText.ToLower());
}

Ого, обработка исключений Pokemon скроет такие проблемы, как ThreadAbortException и т. д. Пожалуйста, поймите что-нибудь конкретное.

JBRWilkinson 26.07.2010 13:06

Больше примеров можно найти здесь: www.extensionmethod.net

Я устал от утомительной проверки на null при извлечении значений из MySqlDataReader, поэтому:

public static DateTime? GetNullableDateTime(this MySqlDataReader dr, string fieldName)
{
    DateTime? nullDate = null;
    return dr.IsDBNull(dr.GetOrdinal(fieldName)) ? nullDate : dr.GetDateTime(fieldName);
}

public static string GetNullableString(this MySqlDataReader dr, string fieldName)
{
    return dr.IsDBNull(dr.GetOrdinal(fieldName)) ? String.Empty : dr.GetString(fieldName);
}

public static char? GetNullableChar(this MySqlDataReader dr, string fieldName)
{
    char? nullChar = null;
    return dr.IsDBNull(dr.GetOrdinal(fieldName)) ? nullChar : dr.GetChar(fieldName);
}

Конечно, это можно использовать с любым SqlDataReader.


И Хэнги, и Джо дали несколько хороших комментариев о том, как это сделать, и с тех пор у меня была возможность реализовать что-то подобное в другом контексте, так что вот еще одна версия:

public static int? GetNullableInt32(this IDataRecord dr, int ordinal)
{
    int? nullInt = null;
    return dr.IsDBNull(ordinal) ? nullInt : dr.GetInt32(ordinal);
}

public static int? GetNullableInt32(this IDataRecord dr, string fieldname)
{
    int ordinal = dr.GetOrdinal(fieldname);
    return dr.GetNullableInt32(ordinal);
}

public static bool? GetNullableBoolean(this IDataRecord dr, int ordinal)
{
    bool? nullBool = null;
    return dr.IsDBNull(ordinal) ? nullBool : dr.GetBoolean(ordinal);
}

public static bool? GetNullableBoolean(this IDataRecord dr, string fieldname)
{
    int ordinal = dr.GetOrdinal(fieldname);
    return dr.GetNullableBoolean(ordinal);
}

Это также должно работать как метод расширения для IDataReader.

hangy 08.11.2008 16:07

Собственно, сделайте параметр this типа IDataRecord для максимальной совместимости. В моей версии у меня есть перегрузка, которая принимает порядковый номер, который вызывает версия fieldName. Сохраняет «GetOrdinal» с последующим поиском по имени.

Joel Mueller 11.11.2008 05:20

Существует подходящая реализация, которая может работать с любым типом значения: rabdullin.com/journal/2008/12/6/…

Rinat Abdullin 06.12.2008 13:37

Спасибо, Ринат, я фактически свел это к одному универсальному методу - см. stackoverflow.com/questions/303287

Adam Lassek 08.12.2008 20:04

Все эти методы кажутся ненужными, поскольку вы можете использовать ключевое слово as, чтобы получить значение от считывателя, допускающее значение null. Если вы комбинируете нулевой объединяющий оператор ?? с оператором as, вы можете даже иметь ненулевое значение по умолчанию для прямого перехода к типу значения. См. stackoverflow.com/questions/746767/…

stevehipwell 14.05.2010 13:47

@Stevo Нет, вы не можете использовать as таким образом. Вы это проверяли? Значения DBNull вызовут исключение.

Adam Lassek 18.05.2010 03:49

@Adam Lassek - На самом деле да, я тестировал это и запускал в производственной среде. Я понятия не имею, откуда у вас создалось впечатление, что будет сгенерировано исключение, если значение равно DBNull, это действительно не так; то есть доступ к данным 101.

stevehipwell 18.05.2010 13:01

@Steve, я думаю, что это зависит от провайдера .. У меня были провайдеры оракулов, которые взорвали баллы DBNull, но некоторые другие этого не сделали. Хотя слишком долго, я, наверное, ошибаюсь;)

Brady Moritz 21.07.2010 20:37

Я рекомендую посмотреть, можно ли это сделать для строго типизированных таблиц данных? они также не допускают типов, допускающих значение NULL, и с ними очень сложно работать. На самом деле они намного хуже, потому что, если вы попытаетесь получить доступ к полю с нулевым значением, сам вызовет исключение ... он даже не вернет dbnull, который вы можете проверить.

Brady Moritz 21.07.2010 20:38

Я использую их в своих веб-проектах, в основном с MVC. У меня есть несколько из них, написанных для ViewData и TempData

/// <summary>
/// Checks the Request.QueryString for the specified value and returns it, if none 
/// is found then the default value is returned instead
/// </summary>
public static T QueryValue<T>(this HtmlHelper helper, string param, T defaultValue) {
    object value = HttpContext.Current.Request.QueryString[param] as object;
    if (value == null) { return defaultValue; }
    try {
        return (T)Convert.ChangeType(value, typeof(T));
    } catch (Exception) {
        return defaultValue;
    }
}

Так я могу написать что-то вроде ...

<% if (Html.QueryValue("login", false)) { %>
    <div>Welcome Back!</div>

<% } else { %>
    <%-- Render the control or something --%>

<% } %>

Кто угодно может его использовать - угощайтесь сами

hugoware 09.11.2008 01:38

Очень хорошо, может быть расширен, чтобы сначала проверить строку запроса, затем проверить ViewData, затем проверить SessionState и, наконец, вернуть значение по умолчанию.

John 22.04.2010 17:50

Мои расширения преобразования, которые позволяют:

int i = myString.To<int>();

Вот оно, как размещено на TheSoftwareJedi.com

public static T To<T>(this IConvertible obj)
{
  return (T)Convert.ChangeType(obj, typeof(T));
}

public static T ToOrDefault<T>
             (this IConvertible obj)
{
    try
    {
        return To<T>(obj);
    }
    catch
    {
        return default(T);
    }
}

public static bool ToOrDefault<T>
                    (this IConvertible obj,
                     out T newObj)
{
    try
    {
        newObj = To<T>(obj); 
        return true;
    }
    catch
    {
        newObj = default(T); 
        return false;
    }
}

public static T ToOrOther<T>
                       (this IConvertible obj,
                       T other)
{
  try
  {
      return To<T>obj);
  }
  catch
  {
      return other;
  }
}

public static bool ToOrOther<T>
                         (this IConvertible obj,
                         out T newObj,
                         T other)
{
    try
    {
        newObj = To<T>(obj);
        return true;
    }
    catch
    {
        newObj = other;
        return false;
    }
}

public static T ToOrNull<T>
                      (this IConvertible obj)
                      where T : class
{
    try
    {
        return To<T>(obj);
    }
    catch
    {
        return null;
    }
}

public static bool ToOrNull<T>
                  (this IConvertible obj,
                  out T newObj)
                  where T : class
{
    try
    {
        newObj = To<T>(obj);
        return true;
    }
    catch
    {
        newObj = null;
        return false;
    }
}

Вы можете запросить значение по умолчанию (вызывает пустой конструктор или «0» для числовых значений) в случае ошибки, указать значение «по умолчанию» (я называю его «другим») или запросить значение null (где T: класс). Я также предоставил как модели скрытых исключений, так и типичную модель TryParse, которая возвращает логическое значение, указывающее на предпринятые действия, а параметр out содержит новое значение. Итак, наш код может делать такие вещи

int i = myString.To<int>();
string a = myInt.ToOrDefault<string>();
//note type inference
DateTime d = myString.ToOrOther(DateTime.MAX_VALUE);
double d;
//note type inference
bool didItGiveDefault = myString.ToOrDefault(out d);
string s = myDateTime.ToOrNull<string>();

Я не мог заставить типы Nullable полностью вписаться во все это. Я пробовал около 20 минут, прежде чем бросить полотенце.

Лично я не поклонник кода, который пытается / уловить, чтобы определить результат. Try / catch следует использовать для ошибок, которые возникают вне предполагаемой логики, ИМО. хммммм

Pure.Krome 09.11.2008 15:49

Если бы я не хотел, чтобы вы использовали код, я бы его не опубликовал! :)

TheSoftwareJedi 12.11.2008 01:20

Наконец-то что-то невидимое. Мне это нравится. :)

Arnis Lapsa 02.11.2009 22:03

Вы должны, по крайней мере, изменить это предложение «catch», чтобы перехватить только те исключения, которые вызовет ChangeType (), когда он не может «преобразовать» ссылку. Я думаю, вы не хотели бы, чтобы какие-либо OutOfMemoryException, ExecutionEngineException, ThreadAbortException или тому подобное рассматривались как ошибка преобразования. В противном случае было бы довольно сложно отслеживать ошибки.

Christian.K 22.11.2009 21:17

Я считаю, что ToOrNull ведет себя точно так же, как ToOrDefault (т.е. если вы вызываете ToOrDefault для ссылочного типа с неудачным преобразованием, он вернет null). Но что более важно, мне это кажется излишним, поскольку var s = myObject as string выполняет то же самое, что и var s = myObject.ToOrNull<string>(), но без потенциальной необходимости ловить InvalidCastException. Я что-то упускаю?

Dan Tao 14.03.2010 00:23

ForEach для IEnumerables

public static class FrameworkExtensions
{
    // a map function
    public static void ForEach<T>(this IEnumerable<T> @enum, Action<T> mapFunction)
    {
        foreach (var item in @enum) mapFunction(item);
    }
}

Наивный пример:

var buttons = GetListOfButtons() as IEnumerable<Button>;

// click all buttons
buttons.ForEach(b => b.Click());

Классный пример:

// no need to type the same assignment 3 times, just
// new[] up an array and use foreach + lambda
// everything is properly inferred by csc :-)
new { itemA, itemB, itemC }
    .ForEach(item => {
        item.Number = 1;
        item.Str = "Hello World!";
    });

Примечание:

Это не похоже на Select, потому что Selectожидает ваша функция возвращает что-то, как для преобразования в другой список.

ForEach просто позволяет вам выполнять что-то для каждого из элементов без каких-либо преобразований / манипуляций с данными.

Я сделал это, чтобы программировать в более функциональном стиле, и был удивлен, что List имеет ForEach, а IEnumerable - нет.

Поместите это в проект codeplex

@bovium конечно ... @Atomiton я думаю ... но я недостаточно хорошо знаю Руби, чтобы быть уверенным.

chakrit 09.11.2008 17:14

+1. Я не знаю, почему .NET BCL не имеет расширения .ForEach для IEnumerable <T>

Judah Gabriel Himango 12.11.2008 02:40

Я разместил это где-то еще на SO, но кто-то сбил. Как-то связано с тем, что он завершает выражение (т.е. не возвращает IEnumerable <T>).

Jonathan C Dickinson 08.01.2009 10:32

Сообщение о том, почему расширения LINQ IEnumerable <T> не включают ForEach: stackoverflow.com/questions/317874/…

Neil 09.06.2009 20:50

Я рекомендую прочитать это перед использованием метода: blogs.msdn.com/ericlippert/archive/2009/05/18/…

jpbochi 06.01.2010 23:50

Реактивные расширения включают IEnumerable <T> .Run () со многими перегрузками. Очевидно, группа Эрика Мейера не в ладах с группой Эрика Липперта.

scobi 14.10.2010 20:31

@jpbochi: Это просто демагогия Microsoft

abatishchev 22.10.2010 19:10

@abatishchev И ваш комментарий - это просто предубеждение против Microsoft. Это не отменяет ни одного слова, написанного Эриком. Чьи-то аргументы не считаются действительными или недействительными только из-за компании, в которой он / она работает.

jpbochi 25.10.2010 19:44

Между прочим, позвольте мне прояснить один момент. Я не говорил, что вам не следует использовать этот метод расширения ForEach. Я просто сказал, что вам следует рассмотреть моменты, которые раскрыл Эрик, прежде чем вы решите, использовать это или нет. Я прочитал и решил не использовать. Вы можете делать со своим кодом все, что хотите.

jpbochi 25.10.2010 19:45

@jpbochi: Я очень уважаю Эрика. Тем не менее он зачитывает официальную архитектурную позицию команды Microsoft, которую я назвал демагогией.

abatishchev 25.10.2010 20:19

ReactiveExtensions предназначены для «реактивного» программирования. Естественно, если вы разбираетесь в «реактивном» программировании, вам потребуется метод «Run ()». Между группами нет никаких разногласий. Они просто предлагают два разных способа кодирования с использованием двух разных фреймворков.

chakrit 26.10.2010 10:04

Функция для сравнения файлов / каталогов через Информация о файловой системе ОС. Это полезно для сравнения общих ресурсов с локальными файлами.

Использование:

DirectoryInfo dir = new DirectoryInfo(@"C:\test\myShareDir");
Console.WriteLine(dir.IsSameFileAs(@"\\myMachineName\myShareDir"));

FileInfo file = new FileInfo(@"C:\test\myShareDir\file.txt");
Console.WriteLine(file.IsSameFileAs(@"\\myMachineName\myShareDir\file.txt"));

Код:

public static class FileExtensions
{
    struct BY_HANDLE_FILE_INFORMATION
    {
        public uint FileAttributes;
        public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
        public uint VolumeSerialNumber;
        public uint FileSizeHigh;
        public uint FileSizeLow;
        public uint NumberOfLinks;
        public uint FileIndexHigh;
        public uint FileIndexLow;
    }

    //
    // CreateFile constants
    //
    const uint FILE_SHARE_READ = 0x00000001;
    const uint OPEN_EXISTING = 3;
    const uint GENERIC_READ = (0x80000000);
    const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;


    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr CreateFile(
        string lpFileName,
        uint dwDesiredAccess,
        uint dwShareMode,
        IntPtr lpSecurityAttributes,
        uint dwCreationDisposition,
        uint dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);

    public static bool IsSameFileAs(this FileSystemInfo file, string path)
    {
        BY_HANDLE_FILE_INFORMATION fileInfo1, fileInfo2;
        IntPtr ptr1 = CreateFile(file.FullName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
        if ((int)ptr1 == -1)
        {
            System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            throw e;
        }
        IntPtr ptr2 = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
        if ((int)ptr2 == -1)
        {
            System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            throw e;
        }
        GetFileInformationByHandle(ptr1, out fileInfo1);
        GetFileInformationByHandle(ptr2, out fileInfo2);

        return ((fileInfo1.FileIndexHigh == fileInfo2.FileIndexHigh) &&
            (fileInfo1.FileIndexLow == fileInfo2.FileIndexLow));
    }
}

При этом не используются методы расширения. Это просто статический класс.

bovium 08.11.2008 21:31

Чтобы изменить метод расширения: static public bool CompareFiles (string path1, string path2) на static public bool IsSameFileAs (this string path1, string path2); затем используйте как: if (file1.IsSameFileAs (file2)

Armstrongest 09.11.2008 07:53

Два разных файла на разных дисках могут по совпадению иметь один и тот же FileIndex. Вам также нужно сравнить VolumeSerialNumber - но тогда ваш пример не удастся, поскольку VolumeSerialNumbers разные.

Rasmus Faber 08.01.2009 12:53

См. Также stackoverflow.com/questions/410705/…

Rasmus Faber 08.01.2009 12:59

Разве вы не должны закрыть эти дескрипторы файлов?

marijne 16.11.2009 18:41

Я не хотел добавлять ничего из того, что уже было сказано, поэтому вот некоторые из них, которые я использую, но не упомянутые. (Извините, если это слишком долго):

public static class MyExtensions
{
    public static bool IsInteger(this string input)
    {
        int temp;

        return int.TryParse(input, out temp);
    }

    public static bool IsDecimal(this string input)
    {
        decimal temp;

        return decimal.TryParse(input, out temp);
    }

    public static int ToInteger(this string input, int defaultValue)
    {
        int temp;

        return (int.TryParse(input, out temp)) ? temp : defaultValue;
    }

    public static decimal ToDecimal(this string input, decimal defaultValue)
    {
        decimal temp;

        return (decimal.TryParse(input, out temp)) ? temp : defaultValue;
    }

    public static DateTime ToFirstOfTheMonth(this DateTime input)
    {
        return input.Date.AddDays(-1 * input.Day + 1);
    }

    // Intentionally returns 0 if the target date is before the input date.
    public static int MonthsUntil(this DateTime input, DateTime targetDate)
    {
        input = input.ToFirstOfTheMonth();

        targetDate = targetDate.ToFirstOfTheMonth();

        int result = 0;

        while (input < targetDate)
        {
        input = input.AddMonths(1);
            result++;
        }

        return result;
    }

    // Used for backwards compatibility in a system built before my time.
    public static DataTable ToDataTable(this IEnumerable input)
    {
        // too much code to show here right now...
    }
}

Принимает camelCaseWord или PascalCaseWord и «переводит их в слова», то есть camelCaseWord => верблюжье слово регистра

public static string Wordify( this string camelCaseWord )
{
    // if the word is all upper, just return it
    if ( !Regex.IsMatch( camelCaseWord, "[a-z]" ) )
        return camelCaseWord;

    return string.Join( " ", Regex.Split( camelCaseWord, @"(?<!^)(?=[A-Z])" ) );
}

Я часто использую его вместе с Capitalize

public static string Capitalize( this string word )
{
    return word[0].ToString( ).ToUpper( ) + word.Substring( 1 );
}

Пример использования

SomeEntityObject entity = DataAccessObject.GetSomeEntityObject( id );
List<PropertyInfo> properties = entity.GetType().GetPublicNonCollectionProperties( );

// wordify the property names to act as column headers for an html table or something
List<string> columns = properties.Select( p => p.Name.Capitalize( ).Wordify( ) ).ToList( );

Бесплатное использование в проекте codeplex

Агрегат в Capitalize очень плохо сказывается на производительности, потому что он создает много экземпляров строк. Почему бы вместо этого не использовать word.Substring (1)?

Thomas Levesque 20.11.2009 03:16
public static class EnumerableExtensions
{
    [Pure]
    public static U MapReduce<T, U>(this IEnumerable<T> enumerable, Func<T, U> map, Func<U, U, U> reduce)
    {
        CodeContract.RequiresAlways(enumerable != null);
        CodeContract.RequiresAlways(enumerable.Skip(1).Any());
        CodeContract.RequiresAlways(map != null);
        CodeContract.RequiresAlways(reduce != null);
        return enumerable.AsParallel().Select(map).Aggregate(reduce);
    }
    [Pure]
    public static U MapReduce<T, U>(this IList<T> list, Func<T, U> map, Func<U, U, U> reduce)
    {
        CodeContract.RequiresAlways(list != null);
        CodeContract.RequiresAlways(list.Count >= 2);
        CodeContract.RequiresAlways(map != null);
        CodeContract.RequiresAlways(reduce != null);
        U result = map(list[0]);
        for (int i = 1; i < list.Count; i++)
        {
            result = reduce(result,map(list[i]));
        }
        return result;
    }

    //Parallel version; creates garbage
    [Pure]
    public static U MapReduce<T, U>(this IList<T> list, Func<T, U> map, Func<U, U, U> reduce)
    {
        CodeContract.RequiresAlways(list != null);
        CodeContract.RequiresAlways(list.Skip(1).Any());
        CodeContract.RequiresAlways(map != null);
        CodeContract.RequiresAlways(reduce != null);

        U[] mapped = new U[list.Count];
        Parallel.For(0, mapped.Length, i =>
            {
                mapped[i] = map(list[i]);
            });
        U result = mapped[0];
        for (int i = 1; i < list.Count; i++)
        {
            result = reduce(result, mapped[i]);
        }
        return result;
    }

}

+1 за использование малоизвестных API .NET 4.0 для разработки по контракту.

Judah Gabriel Himango 12.11.2008 02:38

Разве не опасно уже перечислять "enumerable", вызывая "Count" при проверке контракта? Или это не проверка во время выполнения?

flq 09.05.2009 20:38

Это по-прежнему опасно, потому что некоторые перечисления можно повторять только один раз, но я исправил это так, что, по крайней мере, он останавливается после двух итераций, а не определяет все количество.

Joel Coehoorn 06.05.2010 17:26

Пифонические методы для словарей:

/// <summary>
/// If a key exists in a dictionary, return its value, 
/// otherwise return the default value for that type.
/// </summary>
public static U GetWithDefault<T, U>(this Dictionary<T, U> dict, T key)
{
    return dict.GetWithDefault(key, default(U));
}

/// <summary>
/// If a key exists in a dictionary, return its value,
/// otherwise return the provided default value.
/// </summary>
public static U GetWithDefault<T, U>(this Dictionary<T, U> dict, T key, U defaultValue)
{
    return dict.ContainsKey(key)
        ? dict[key]
        : defaultValue;
}

Полезно, когда вы хотите добавить метку времени к имени файла, чтобы гарантировать уникальность.

/// <summary>
/// Format a DateTime as a string that contains no characters
//// that are banned from filenames, such as ':'.
/// </summary>
/// <returns>YYYY-MM-DD_HH.MM.SS</returns>
public static string ToFilenameString(this DateTime dt)
{
    return dt.ToString("s").Replace(":", ".").Replace('T', '_');
}

Используйте dt.ToString("yyy-MM-dd_HH.mm.ss"); напрямую, чтобы избежать создания двух дополнительных экземпляров String. Поскольку этот формат не включает компонент часового пояса, время в формате UTC было бы лучше через dt.ToUniversalTime().ToString(...).

devstuff 15.12.2008 07:49

Лучше использовать TryGetValue, вы выполняете два поиска вместо одного.

Anton Tykhyy 07.05.2009 12:04

«Пожалуйста, отметьте свои ответы согласием на включение кода в проект Codeplex».

Почему? Все материалы на этом сайте под CC-by-sa-2.5, так что просто поместите свой Extension overflow Project под ту же лицензию, и вы сможете свободно использовать его.

Во всяком случае, вот функция String.Reverse, основанная на этот вопрос.

/// <summary>
/// Reverse a String
/// </summary>
/// <param name = "input">The string to Reverse</param>
/// <returns>The reversed String</returns>
public static string Reverse(this string input)
{
    char[] array = input.ToCharArray();
    Array.Reverse(array);
    return new string(array);
}

Разве String уже не реализует IEnumerable <char>? Так что вам просто нужно сделать return new String (input.Reverse ());

Iain Galloway 02.07.2010 19:13

Реализация с использованием StringBuilder должна быть быстрее.

CodesInChaos 14.10.2010 15:04

@CodeInChaos Тестирование в stackoverflow.com/questions/228038 показало, что StringBuilder работает медленнее.

Michael Stum 14.10.2010 20:23

Ты прав. Похоже, что требования к безопасности потоков (вероятно, для обеспечения неизменности строки, возвращаемой ToString) сильно замедляют StringBuilder.

CodesInChaos 14.10.2010 21:02

Надеюсь, вы не встретите суррогатов или комбинирующих персонажей.

dalle 14.10.2010 23:03

Или даже строки новой строки в форме "\r\n". Или двойные символы новой строки в форме "\n\r", если на то пошло.

leviathanbadger 21.03.2012 05:28

У меня есть метод расширения для регистрации исключений:

public static void Log(this Exception obj)
{
  //your logging logic here
}

И используется он так:

try
{
    //Your stuff here
}
catch(Exception ex)
{
    ex.Log();
}

[извините за отправку дважды; второй лучше спроектирован :-)]

Может быть, следует читать общедоступный статический журнал void (этот объект Exception) {}?

Chris S 28.01.2009 20:03

Я думаю, что это хорошо для BCL или сторонних исключений, но если вы откатываете свои собственные типы исключений, вы можете поместить ведение журнала в свой базовый класс исключений. Таким образом, вам не нужно помнить о вызове Log ().

si618 04.06.2010 10:13

Вот еще одна реализация ThrowIfNull:

[ThreadStatic]
private static string lastMethodName = null;

[ThreadStatic]
private static int lastParamIndex = 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowIfNull<T>(this T parameter)
{
    var currentStackFrame = new StackFrame(1);
    var props = currentStackFrame.GetMethod().GetParameters();

    if (!String.IsNullOrEmpty(lastMethodName)) {
        if (currentStackFrame.GetMethod().Name != lastMethodName) {
            lastParamIndex = 0;
        } else if (lastParamIndex >= props.Length - 1) {
            lastParamIndex = 0;
        } else {
            lastParamIndex++;
        }
    } else {
        lastParamIndex = 0;
    }

    if (!typeof(T).IsValueType) {
        for (int i = lastParamIndex; i &lt; props.Length; i++) {
            if (props[i].ParameterType.IsValueType) {
                lastParamIndex++;
            } else {
                break;
            }
        }
    }

    if (parameter == null) {
        string paramName = props[lastParamIndex].Name;
        throw new ArgumentNullException(paramName);
    }

    lastMethodName = currentStackFrame.GetMethod().Name;
}

Это не так эффективно, как другие реализации, но имеет более чистое использование:

public void Foo()
{
    Bar(1, 2, "Hello", "World"); //no exception
    Bar(1, 2, "Hello", null); //exception
    Bar(1, 2, null, "World"); //exception
}

public void Bar(int x, int y, string someString1, string someString2)
{
    //will also work with comments removed
    //x.ThrowIfNull();
    //y.ThrowIfNull();
    someString1.ThrowIfNull();
    someString2.ThrowIfNull();

    //Do something incredibly useful here!
}

Изменить параметры на int? тоже будет работать.

-законопроект

на самом деле он был завершен, но меньше, чем в цикле for, было интерпретировано как тег ... просто изменил его на <

11.11.2008 22:15

Я использую его довольно часто ...

Исходный код:

if (guid != Guid.Empty) return guid;
else return Guid.NewGuid();

Новый код:

return guid.NewGuidIfEmpty();

Способ расширения:

public static Guid NewGuidIfEmpty(this Guid uuid)
{
    return (uuid != Guid.Empty ? uuid : Guid.NewGuid());
}

Может быть что-то вроде ... guid.NewGuidIfEmpty (); возвратный гид; ... могло бы быть лучше с семантической точки зрения.

Victor Rodrigues 14.05.2009 22:31

Я разочарован тем, что .NET Framework предпочитает, чтобы файлы и каталоги представлялись в виде строк, а не объектов, и что типы FileInfo и DirectoryInfo не так эффективны, как хотелось бы. Итак, я начал писать свободные методы расширения по мере необходимости, например:

public static FileInfo SetExtension(this FileInfo fileInfo, string extension)
{
    return new FileInfo(Path.ChangeExtension(fileInfo.FullName, extension));
}

public static FileInfo SetDirectory(this FileInfo fileInfo, string directory)
{
    return new FileInfo(Path.Combine(directory, fileInfo.Name));
}

Да, вы можете поместить это в кодовый комплекс

FileInfo и DirectoryInfo довольно медленны по сравнению с их строковыми аналогами File и Directory. Возможно, вы захотите профилировать их.

chakrit 18.01.2009 23:40

Этот для MVC добавляет возможность генерировать тег <label /> к переменной Html, которая доступна в каждом ViewPage. Надеюсь, он будет полезен другим, пытающимся разработать подобные расширения.

Использовать:

<%= Html.Label("LabelId", "ForId", "Text")%>

Выход:

<label id = "LabelId" for = "ForId">Text</label>

Код:

public static class HtmlHelperExtensions
{
    public static string Label(this HtmlHelper Html, string @for, string text)
    {
        return Html.Label(null, @for, text);
    }

    public static string Label(this HtmlHelper Html, string @for, string text, object htmlAttributes)
    {
        return Html.Label(null, @for, text, htmlAttributes);
    }

    public static string Label(this HtmlHelper Html, string @for, string text, IDictionary<string, object> htmlAttributes)
    {
        return Html.Label(null, @for, text, htmlAttributes);
    }

    public static string Label(this HtmlHelper Html, string id, string @for, string text)
    {
        return Html.Label(id, @for, text, null);
    }

    public static string Label(this HtmlHelper Html, string id, string @for, string text, object htmlAttributes)
    {
        return Html.Label(id, @for, text, new RouteValueDictionary(htmlAttributes));
    }

    public static string Label(this HtmlHelper Html, string id, string @for, string text, IDictionary<string, object> htmlAttributes)
    {
        TagBuilder tag = new TagBuilder("label");

        tag.MergeAttributes(htmlAttributes);

        if (!string.IsNullOrEmpty(id))
            tag.MergeAttribute("id", Html.AttributeEncode(id));

        tag.MergeAttribute("for", Html.AttributeEncode(@for));

        tag.SetInnerText(Html.Encode(text));

        return tag.ToString(TagRenderMode.Normal);
    }
}

Проверьте MvcContrib.FluentHtml

Arnis Lapsa 02.11.2009 22:07

Вероятно, это следует продублировать с помощью Literal.

Mark Hurd 03.09.2010 11:05

Метод Substring в строковом классе всегда казался мне неадекватным. Обычно, когда вы делаете подстроку, вы знаете символ (символы), с которого вы хотите начать, и символы, где вы хотите закончить. Таким образом, я всегда считал, что указывать длину в качестве второго параметра глупо. Поэтому я написал свои собственные методы расширения. Тот, который принимает startIndex и endIndex. И один, который принимает startText (строка) и endText (строка), поэтому вы можете просто указать текст, с которого начать подстроку, и текст, где ее закончить.

ПРИМЕЧАНИЕ. Я не мог назвать метод Substring, как в .NET, потому что моя первая перегрузка принимает те же типы параметров, что и одна из перегрузок .NET. Поэтому я назвал их Subsetstring. Не стесняйтесь добавлять в CodePlex ...

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 > endIndex)
        {
            throw new InvalidOperationException("End Index must be after Start Index.");
        }

        if (startIndex < 0)
        {
            throw new InvalidOperationException("Start Index must be a positive number.");
        }

        if (endIndex <0)
        {
            throw new InvalidOperationException("End Index must be a positive number.");
        }

        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) || string.IsNullOrEmpty(endText))
        {
            throw new ArgumentException("Start Text and End Text cannot be empty.");
        }
        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);
    }
}

Использование:

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

Вывод: «тестер для моего»

Некоторые из моих лучших расширений методов (у меня их много!):

public static T ToEnum<T>(this string str) where T : struct
{
    return (T)Enum.Parse(typeof(T), str);
}

//DayOfWeek sunday =  "Sunday".ToEnum<DayOfWeek>();

public static string ToString<T>(this IEnumerable<T> collection, string separator)
{
    return ToString(collection, t => t.ToString(), separator);
}

public static string ToString<T>(this IEnumerable<T> collection, Func<T, string> stringElement, string separator)
{
    StringBuilder sb = new StringBuilder();
    foreach (var item in collection)
    {
        sb.Append(stringElement(item));
        sb.Append(separator);
    }
    return sb.ToString(0, Math.Max(0, sb.Length - separator.Length));  // quita el ultimo separador
}

//new []{1,2,3}.ToString(i=>i*2, ", ")  --> "2, 4, 6"

Кроме того, следующие предназначены для того, чтобы иметь возможность продолжать работу в той же строке практически в любой ситуации, не объявляя новые переменные, а затем удаляя состояние:

public static R Map<T, R>(this T t, Func<T, R> func)
{
    return func(t);
}

ExpensiveFindWally().Map(wally=>wally.FirstName + " " + wally.LastName)

public static R TryCC<T, R>(this T t, Func<T, R> func)
    where T : class
    where R : class
{
    if (t == null) return null;
    return func(t);
}

public static R? TryCS<T, R>(this T t, Func<T, R> func)
    where T : class
    where R : struct
{
    if (t == null) return null;
    return func(t);
}

public static R? TryCS<T, R>(this T t, Func<T, R?> func)
    where T : class
    where R : struct
{
    if (t == null) return null;
    return func(t);
}

public static R TrySC<T, R>(this T? t, Func<T, R> func)
    where T : struct
    where R : class
{
    if (t == null) return null;
    return func(t.Value);
}

public static R? TrySS<T, R>(this T? t, Func<T, R> func)
    where T : struct
    where R : struct
{
    if (t == null) return null;
    return func(t.Value);
}

public static R? TrySS<T, R>(this T? t, Func<T, R?> func)
    where T : struct
    where R : struct
{
    if (t == null) return null;
    return func(t.Value);
}

//int? bossNameLength =  Departament.Boss.TryCC(b=>b.Name).TryCS(s=>s.Length);


public static T ThrowIfNullS<T>(this T? t, string mensaje)
    where T : struct
{
    if (t == null)
        throw new NullReferenceException(mensaje);
    return t.Value;
}

public static T ThrowIfNullC<T>(this T t, string mensaje)
    where T : class
{
    if (t == null)
        throw new NullReferenceException(mensaje);
    return t;
}

public static T Do<T>(this T t, Action<T> action)
{
    action(t);
    return t;
}

//Button b = new Button{Content = "Click"}.Do(b=>Canvas.SetColumn(b,2));

public static T TryDo<T>(this T t, Action<T> action) where T : class
{
    if (t != null)
        action(t);
    return t;
}

public static T? TryDoS<T>(this T? t, Action<T> action) where T : struct
{
    if (t != null)
        action(t.Value);
    return t;
}

Надеюсь, это не похоже на Марс :)

Вот и от римских цифр. Используется не часто, но может пригодиться. Использование:

if ("IV".IsValidRomanNumeral())
{
   // Do useful stuff with the number 4.
}

Console.WriteLine("MMMDCCCLXXXVIII".ParseRomanNumeral());
Console.WriteLine(3888.ToRomanNumeralString());

Источник:

    public static class RomanNumeralExtensions
    {
        private const int NumberOfRomanNumeralMaps = 13;

        private static readonly Dictionary<string, int> romanNumerals =
            new Dictionary<string, int>(NumberOfRomanNumeralMaps)
            {
                { "M", 1000 }, 
                { "CM", 900 }, 
                { "D", 500 }, 
                { "CD", 400 }, 
                { "C", 100 }, 
                { "XC", 90 }, 
                { "L", 50 }, 
                { "XL", 40 }, 
                { "X", 10 }, 
                { "IX", 9 }, 
                { "V", 5 }, 
                { "IV", 4 }, 
                { "I", 1 }
            };

        private static readonly Regex validRomanNumeral = new Regex(
            "^(?i:(?=[MDCLXVI])((M{0,3})((C[DM])|(D?C{0,3}))"
            + "?((X[LC])|(L?XX{0,2})|L)?((I[VX])|(V?(II{0,2}))|V)?))$", 
            RegexOptions.Compiled);

        public static bool IsValidRomanNumeral(this string value)
        {
            return validRomanNumeral.IsMatch(value);
        }

        public static int ParseRomanNumeral(this string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            value = value.ToUpperInvariant().Trim();

            var length = value.Length;

            if ((length == 0) || !value.IsValidRomanNumeral())
            {
                throw new ArgumentException("Empty or invalid Roman numeral string.", "value");
            }

            var total = 0;
            var i = length;

            while (i > 0)
            {
                var digit = romanNumerals[value[--i].ToString()];

                if (i > 0)
                {
                    var previousDigit = romanNumerals[value[i - 1].ToString()];

                    if (previousDigit < digit)
                    {
                        digit -= previousDigit;
                        i--;
                    }
                }

                total += digit;
            }

            return total;
        }

        public static string ToRomanNumeralString(this int value)
        {
            const int MinValue = 1;
            const int MaxValue = 3999;

            if ((value < MinValue) || (value > MaxValue))
            {
                throw new ArgumentOutOfRangeException("value", value, "Argument out of Roman numeral range.");
            }

            const int MaxRomanNumeralLength = 15;
            var sb = new StringBuilder(MaxRomanNumeralLength);

            foreach (var pair in romanNumerals)
            {
                while (value / pair.Value > 0)
                {
                    sb.Append(pair.Key);
                    value -= pair.Value;
                }
            }

            return sb.ToString();
        }
    }

Это напоминает мне Python PEP 313, который был первоапрельской шуткой по включению литералов римских цифр в python: python.org/dev/peps/pep-0313

torial 07.08.2010 20:22

Некоторые расширения для работы со списками:

/// <summary>
/// Wrap an object in a list
/// </summary>
public static IList<T> WrapInList<T>(this T item)
{
    List<T> result = new List<T>();
    result.Add(item);
    return result;
}

используйте, например:

myList = someObject.InList();

Сделать IEnumerable, который содержит элементы из одного или нескольких источников, чтобы IEnumerable работал больше как списки. Это может быть не очень хорошей идеей для высокопроизводительного кода, но полезно для проведения тестов:

public static IEnumerable<T> Append<T>(this IEnumerable<T> enumerable, T newItem)
{
    foreach (T item in enumerable)
    {
        yield return item;
    }

    yield return newItem;
}

public static IEnumerable<T> Append<T>(this IEnumerable<T> enumerable, params T[] newItems)
{
    foreach (T item in enumerable)
    {
        yield return item;
    }

    foreach (T newItem in newItems)
    {
        yield return newItem;
    }
}

использовать, например,

someEnumeration = someEnumeration.Append(newItem);

Возможны и другие варианты - например,

someEnumeration = someEnumeration.Append(otherEnumeration);

Если вы клонируете элементы, вы также можете клонировать их списки:

public static IList<T> Clone<T>(this IEnumerable<T> source) where T: ICloneable
{
    List<T> result = new List<T>();

    foreach (T item in source)
    {
        result.Add((T)item.Clone());
    }

    return result;
}

Когда я работаю с ObservableCollection<T>, я обычно расширяю его с помощью метода AddRange. Другие ответы здесь дают реализации этого.

Вы можете поместить этот код в проект Codeplex, если хотите.

Почему бы не вернуть значения, используя yield return, и не вернуть «настоящие ленивые» счетчики? В какой-то мере позаботится о вашем высокопроизводительном комментарии к коду.

peSHIr 05.06.2009 10:32

Я бы переименовал их в Append() (и использовал блоки итераторов, как это было предложено peSHlr)

Joel Coehoorn 06.05.2010 17:31

Джоэл, peSHIr: хорошие предложения, я обновил ответ, чтобы сделать это.

Anthony 10.05.2010 13:51

Эти методы расширения очень полезны для меня при анализе ввода формы перед помещением в базу данных.

public static int? ToInt(this string input) 
{
    int val;
    if (int.TryParse(input, out val))
        return val;
    return null;
}

public static DateTime? ToDate(this string input)
{
    DateTime val;
    if (DateTime.TryParse(input, out val))
        return val;
    return null;
}

public static decimal? ToDecimal(this string input)
{
    decimal val;
    if (decimal.TryParse(input, out val))
        return val;
    return null;
}

Мне нравятся эти методы работы с перечислениями, для которых установлен атрибут Flags:

public static bool AnyOf(this object mask, object flags)
{
    return ((int)mask & (int)flags) != 0;
}
public static bool AllOf(this object mask, object flags)
{
    return ((int)mask & (int)flags) == (int)flags;
}
public static object SetOn(this object mask, object flags)
{
    return (int)mask | (int)flags;
}
etc.

Пример использования:

var options = SomeOptions.OptionA;
options = options.SetOn(OptionB);
options = options.SetOn(OptionC);

if (options.AnyOf(SomeOptions.OptionA | SomeOptions.OptionB))
{
etc.

Оригинальные методы были из этой статьи: http://www.codeproject.com/KB/cs/masksandflags.aspx?display=Print Я просто преобразовал их в методы расширения.

Однако одна проблема с ними заключается в том, что параметры типа объекта, что означает, что объекты все в конечном итоге расширяются с помощью этих методов, тогда как в идеале они должны применяться только к перечислениям.

Обновлять Согласно комментариям, вы можете обойти «загрязнение сигнатуры» за счет производительности, например:

public static bool AnyOf(this Enum mask, object flags)
{
    return (Convert.ToInt642(mask) & (int)flags) != 0;
}

Проблемы с этими расширениями: * Все объекты расширены (загрязнение сигнатуры) * накладные расходы на упаковку / распаковку * не все перечисления являются производными от int, могут быть байтовые и длинные

Rinat Abdullin 07.12.2008 12:02

Хорошие моменты, Ринат. Я бы хотел, чтобы был способ написать эти методы расширения.

cbp 13.11.2009 07:54

Если вы хотите сделать это с помощью перечислений, измените тип object на просто Enum.

John Leidegren 31.03.2010 02:18

Расширения, связанные с Timespan, такие как:

public static TimeSpan Seconds(this int seconds)
{
  return TimeSpan.FromSeconds(seconds);
}

public static TimeSpan Minutes(this int minutes)
{
  return TimeSpan.FromMinutes(minutes);
}

Это позволяет использовать:

1.Seconds()
20.Minutes()

Заблокируйте такие расширения, как:

public static IDisposable GetReadLock(this ReaderWriterLockSlim slimLock)
{
  slimLock.EnterReadLock();
  return new DisposableAction(slimLock.ExitReadLock);
}

public static IDisposable GetWriteLock(this ReaderWriterLockSlim slimLock)
{
  slimLock.EnterWriteLock();
  return new DisposableAction(slimLock.ExitWriteLock);
}

public static IDisposable GetUpgradeableReadLock(this ReaderWriterLockSlim slimLock)
{
  slimLock.EnterUpgradeableReadLock();
  return new DisposableAction(slimLock.ExitUpgradeableReadLock);
}

Это позволяет использовать такие блокировки, как:

using (lock.GetUpgradeableReadLock())
{
  // try read
  using (lock.GetWriteLock())
  {
    //do write
  }
}

И многое другое от Общие библиотеки Lokad

Это метод расширения для вспомогательного метода ссылки действия ASP.Net MVC, который позволяет ему использовать атрибуты авторизации контроллера, чтобы решить, должна ли ссылка быть включена, отключена или скрыта из представления текущего пользователя. Я избавляю вас от необходимости заключать ваши ограниченные действия в предложения «если», которые проверяют членство пользователя во всех представлениях. Спасибо Maarten Balliauw за идею и фрагменты кода, которые указали мне путь :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal;
using System.Web.Routing;
using System.Web.Mvc;
using System.Collections;
using System.Reflection;
namespace System.Web.Mvc.Html
{
    public static class HtmlHelperExtensions
    {

        /// <summary>
        /// Shows or hides an action link based on the user's membership status
        /// and the controller's authorize attributes
        /// </summary>
        /// <param name = "linkText">The link text.</param>
        /// <param name = "action">The controller action name.</param>
        /// <param name = "controller">The controller name.</param>
        /// <returns></returns>
        public static string SecurityTrimmedActionLink(
            this HtmlHelper htmlHelper,
            string linkText,
            string action,
            string controller)
        {
            return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, false, null);
        }

        /// <summary>
        /// Enables, disables or hides an action link based on the user's membership status
        /// and the controller's authorize attributes
        /// </summary>
        /// <param name = "linkText">The link text.</param>
        /// <param name = "action">The action name.</param>
        /// <param name = "controller">The controller name.</param>
        /// <param name = "showDisabled">if set to <c>true</c> [show link as disabled - 
        /// using a span tag instead of an anchor tag ].</param>
        /// <param name = "disabledAttributeText">Use this to add attributes to the disabled
        /// span tag.</param>
        /// <returns></returns>
        public static string SecurityTrimmedActionLink(
            this HtmlHelper htmlHelper, 
            string linkText, 
            string action, 
            string controller, 
            bool showDisabled, 
            string disabledAttributeText)
        {
            if (IsAccessibleToUser(action, controller, HttpContext.Current ))
            {
                return htmlHelper.ActionLink(linkText, action, controller);
            }
            else
            {
                return showDisabled ? 
                    String.Format(
                        "<span{1}>{0}</span>", 
                        linkText, 
                        disabledAttributeText==null?"":" "+disabledAttributeText
                        ) : "";
            }
        }

        private static IController GetControllerInstance(string controllerName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            Type controllerType = GetControllerType(controllerName);
            return (IController)Activator.CreateInstance(controllerType);
        }

        private static ArrayList GetControllerAttributes(string controllerName, HttpContext context)
        {
            if (context.Cache[controllerName + "_ControllerAttributes"] == null)
            {
                var controller = GetControllerInstance(controllerName);

                context.Cache.Add(
                    controllerName + "_ControllerAttributes",
                    new ArrayList(controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true)),
                    null,
                    Caching.Cache.NoAbsoluteExpiration,
                    Caching.Cache.NoSlidingExpiration,
                    Caching.CacheItemPriority.Default,
                    null);

            }
            return (ArrayList)context.Cache[controllerName + "_ControllerAttributes"];

        }

        private static ArrayList GetMethodAttributes(string controllerName, string actionName, HttpContext context)
        {
            if (context.Cache[controllerName + "_" + actionName + "_ActionAttributes"] == null)
            {
                ArrayList actionAttrs = new ArrayList();
                var controller = GetControllerInstance(controllerName);
                MethodInfo[] methods = controller.GetType().GetMethods();

                foreach (MethodInfo method in methods)
                {
                    object[] attributes = method.GetCustomAttributes(typeof(ActionNameAttribute), true);

                    if ((attributes.Length == 0 && method.Name == actionName)
                        ||
                        (attributes.Length > 0 && ((ActionNameAttribute)attributes[0]).Name == actionName))
                    {
                        actionAttrs.AddRange(method.GetCustomAttributes(typeof(AuthorizeAttribute), true));
                    }
                }

                context.Cache.Add(
                    controllerName + "_" + actionName + "_ActionAttributes",
                    actionAttrs,
                    null,
                    Caching.Cache.NoAbsoluteExpiration,
                    Caching.Cache.NoSlidingExpiration,
                    Caching.CacheItemPriority.Default,
                    null);

            }

            return (ArrayList)context.Cache[controllerName + "_" + actionName+ "_ActionAttributes"]; 
        }

        public static bool IsAccessibleToUser(string actionToAuthorize, string controllerToAuthorize, HttpContext context)
        {
            IPrincipal principal = context.User;

            //cache the attribute list for both controller class and it's methods

            ArrayList controllerAttributes = GetControllerAttributes(controllerToAuthorize, context);

            ArrayList actionAttributes = GetMethodAttributes(controllerToAuthorize, actionToAuthorize, context);                        

            if (controllerAttributes.Count == 0 && actionAttributes.Count == 0)
                return true;

            string roles = "";
            string users = "";
            if (controllerAttributes.Count > 0)
            {
                AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute;
                roles += attribute.Roles;
                users += attribute.Users;
            }
            if (actionAttributes.Count > 0)
            {
                AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute;
                roles += attribute.Roles;
                users += attribute.Users;
            }

            if (string.IsNullOrEmpty(roles) && string.IsNullOrEmpty(users) && principal.Identity.IsAuthenticated)
                return true;

            string[] roleArray = roles.Split(',');
            string[] usersArray = users.Split(',');
            foreach (string role in roleArray)
            {
                if (role == "*" || principal.IsInRole(role))
                    return true;
            }
            foreach (string user in usersArray)
            {
                if (user == "*" && (principal.Identity.Name == user))
                    return true;
            }
            return false;
        }

        private static Type GetControllerType(string controllerName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            foreach (Type type in assembly.GetTypes())
            {
                if (
                    type.BaseType!=null 
                    && type.BaseType.Name == "Controller" 
                    && (type.Name.ToUpper() == (controllerName.ToUpper() + "Controller".ToUpper())))
                {
                    return type;
                }
            }
            return null;
        }

    }
}
static string Format( this string str,
                    , params Expression<Func<string,object>>[] args)
{
    var parameters = args.ToDictionary
                        ( e=>string.Format("{{{0}}}",e.Parameters[0].Name)
                        , e=>e.Compile()(e.Parameters[0].Name));

    var sb = new StringBuilder(str);
    foreach(var kv in parameters)
    {
        sb.Replace( kv.Key
                  , kv.Value != null ? kv.Value.ToString() : "");
    }

    return sb.ToString();
}

С указанным выше расширением вы можете написать это:

var str = "{foo} {bar} {baz}".Format(foo=>foo, bar=>2, baz=>new object());

и вы получите "foo 2 System.Object ».

Что касается производительности форматирования строк и безумия, вы можете проверить блог Фила Хаака о различных способах этого ... haacked.com/archive/2009/01/14/ named-formats-redux.aspx

WestDiscGolf 22.01.2009 18:38
  • Для добавления нескольких элементов в коллекцию, в которой нет AddRange, например, collection.Add(item1, item2, itemN);

    static void Add<T>(this ICollection<T> coll, params T[] items)
     { foreach (var item in items) coll.Add(item);
     }
    
  • Следующее похоже на string.Format(), но с настраиваемым строковым представлением аргументов, например, "{0} {1} {2}".Format<Custom>(c=>c.Name,"string",new object(),new Custom()) приводит к "string {System.Object} Custom1Name".

    static string Format<T>(  this string format
                            , Func<T,object> select
                            , params object[] args)
     { for(int i=0; i < args.Length; ++i)
        { var x = args[i] as T;
          if (x != null) args[i] = select(x);
        }
       return string.Format(format, args);
     }
    

Просто, но лучше, чем "Enumerable.Range", ИМХО:

/// <summary>
/// Replace "Enumerable.Range(n)" with "n.Range()":
/// </summary>
/// <param name = "n">iterations</param>
/// <returns>0..n-1</returns>
public static IEnumerable<int> Range(this int n)
{
    for (int i = 0; i < n; i++)
        yield return i;
}

Эквивалентно методу Join в Python:

/// <summary>
/// same as python 'join'
/// </summary>
/// <typeparam name = "T">list type</typeparam>
/// <param name = "separator">string separator </param>
/// <param name = "list">list of objects to be ToString'd</param>
/// <returns>a concatenated list interleaved with separators</returns>
static public string Join<T>(this string separator, IEnumerable<T> list)
{
    var sb = new StringBuilder();
    bool first = true;

    foreach (T v in list)
    {
        if (!first)
            sb.Append(separator);
        first = false;

        if (v != null)
            sb.Append(v.ToString());
    }

    return sb.ToString();
}

Вы можете заменить весь код в этой функции на одну строку: return string.Join(separator, list.ToArray());

jpbochi 07.01.2010 00:07

Я использую этот метод расширения обычно с анонимными типами, чтобы получить словарь ala ruby

public static Dictionary<string, object> ToDictionary(this object o)
{
    var dictionary = new Dictionary<string, object>();

    foreach (var propertyInfo in o.GetType().GetProperties())
    {
        if (propertyInfo.GetIndexParameters().Length == 0)
        {
            dictionary.Add(propertyInfo.Name, propertyInfo.GetValue(o, null));
        }
    }

    return dictionary;
}

Вы можете использовать это

var dummy = new { color = "#000000", width = "100%", id = "myid" };
Dictionary<string, object> dict = dummy.ToDictionary();

И с расширенным методом как

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (T item in source)
    {
        action(item);
    }
}

Ты можешь это сделать

dummy.ToDictionary().ForEach((p) => Console.Write("{0}='{1}' ", p.Key, p.Value));

Выход

цвет = '# 000000' ширина = '100%' id = 'myid'

Вы также можете сделать: Console.Write (dummy.ToDictionary (). Select (p => string.Format (p.Key + "= '" + p.Value + "'"))); поэтому нет необходимости в расширении ForEach

chakrit 17.01.2009 08:18

Преобразуйте любую строку в тип Int32

// Calls the underlying int.TryParse method to convert a string
// representation of a number to its 32-bit signed integer equivalent.
// Returns Zero if conversion fails. 
public static int ToInt32(this string s)
{
    int retInt;
    int.TryParse(s, out retInt);
    return retInt;
}

ОБРАЗЕЦ ИСПОЛЬЗОВАНИЯ:
string s = "999";
int i = s.ToInt32();

// Values ordered true/false
// True/false values separated by a capital letter
// Only two values allowed
// ---------------------------
// Limited, but could be useful
public enum BooleanFormat
{
    OneZero,
    YN,
    YesNo,
    TF,
    TrueFalse,
    PassFail,
    YepNope
}

public static class BooleanExtension
{
    /// <summary>
    /// Converts the boolean value of this instance to the specified string value. 
    /// </summary>
    private static string ToString(this bool value, string passValue, string failValue)
    {
        return value ? passValue : failValue;
    }

    /// <summary>
    /// Converts the boolean value of this instance to a string. 
    /// </summary>
    /// <param name = "booleanFormat">A BooleanFormat value. 
    /// Example: BooleanFormat.PassFail would return "Pass" if true and "Fail" if false.</param>
    /// <returns>Boolean formatted string</returns>
    public static string ToString(this bool value, BooleanFormat booleanFormat)
    {
        string booleanFormatString = Enum.GetName(booleanFormat.GetType(), booleanFormat);
        return ParseBooleanString(value, booleanFormatString);      
    }

    // Parses boolean format strings, not optimized
    private static string ParseBooleanString(bool value, string booleanFormatString)
    {
        StringBuilder trueString = new StringBuilder();
        StringBuilder falseString = new StringBuilder();

        int charCount = booleanFormatString.Length;

        bool isTrueString = true;

        for (int i = 0; i != charCount; i++)
        {
            if (char.IsUpper(booleanFormatString[i]) && i != 0)
                isTrueString = false;

            if (isTrueString)
                trueString.Append(booleanFormatString[i]);
            else
                falseString.Append(booleanFormatString[i]);
        }

        return (value == true ? trueString.ToString() : falseString.ToString());
    }

Вы забыли TrueFalseFileNotFound: P

Thomas Levesque 19.08.2010 23:37

Общая попытка:

class Program
{
    static void Main(string[] args)
    {
        var z = 0;
        var a = 0.AsDefaultFor(() => 1 / z);
        Console.WriteLine(a);
        Console.ReadLine();
    }
}

public static class TryExtensions
{
    public static T AsDefaultFor<T>(this T @this, Func<T> operation)
    {
        try
        {
            return operation();
        }
        catch
        {
            return @this;
        }
    }
}

Если хотите, поместите его в проект CodePlex.

Я скучаю по Оператор With Visual Basic при переходе на C#, так что вот оно:

public static void With<T>(this T obj, Action<T> act) { act(obj); }

А вот как его использовать в C#:

someVeryVeryLonggggVariableName.With(x => {
    x.Int = 123;
    x.Str = "Hello";
    x.Str2 = " World!";
});

Экономит много ввода!

Сравните это с:

someVeryVeryLonggggVariableName.Int = 123;
someVeryVeryLonggggVariableName.Str = "Hello";
someVeryVeryLonggggVariableName.Str2 = " World!";

поместить в проект codeplex

Просто предположение, но подумайте, что произойдет, если ваш T является структурой.

Rauhotz 21.01.2009 02:09

Не знаю, но, может быть, там должно быть место T: class.

chakrit 22.01.2009 17:27

Вам по-прежнему нужно указывать имя переменной перед каждым вызовом метода / свойства. Почему бы просто не сократить исходное имя переменной?

Joel Mueller 13.05.2009 19:42

@Joel Muller, это не всегда переменная мой ... подумайте Someone.Else.Namespace.With.WeirdClass.Instance

chakrit 16.05.2009 02:25

Я также использую синтаксис инициализатора свойств C# 3.0 везде, где это возможно, для достижения того же результата.

Steve 21.07.2009 00:41

@chakrit, вот пример. Применяется только при создании объекта Button n = new Button {Name = "Button1", Width = 100, Height = 20, Enabled = true};

Steve 30.07.2009 21:00

Это было бы полезно, когда у вас есть много событий для подключения, потому что синтаксис инициализатора свойств C# не поддерживает события.

Gabe 05.07.2010 20:59

это также полезно вне инициализаторов свойств, потому что вы можете использовать их только при создании нового объекта. это расширение может работать с ранее созданными объектами.

Brady Moritz 21.07.2010 20:41

Итак, что плохого в простой записи { var x = someVeryVeryLonggggVariableName; /* do something with x */ }?

Heinzi 15.10.2010 13:08

@Heinzi Вы получаете новую область видимости (область видимости блока With) и возможность встроенного комплексного доступа к объекту. Кроме того, это не так сильно прерывает ход ваших мыслей, поскольку вам не нужно возвращаться и помещать его в переменную. См .: msdn.microsoft.com/en-us/library/ms172864(VS.80).aspx ...

chakrit 16.10.2010 22:26

Пожалуй, самые полезные методы расширения, которые я написал и использовал, здесь:

http://www.codeproject.com/KB/cs/fun-with-cs-extensions.aspx?msg=2838918#xx2838918xx

Метод WhereIf ()

var query = dc.Reviewer 
    .Where(r => r.FacilityID == facilityID) 
    .WhereIf(CheckBoxActive.Checked, r => r.IsActive); 

public static IEnumerable<TSource> WhereIf<TSource>(
    this IEnumerable<TSource> source,
    bool condition, Func<TSource, bool> predicate) 
{ 
    if (condition) 
        return source.Where(predicate); 
    else 
        return source; 
}

public static IQueryable<TSource> WhereIf<TSource>(
    this IQueryable<TSource> source,
    bool condition, Expression<Func<TSource, bool>> predicate) 
{ 
    if (condition) 
        return source.Where(predicate); 
    else 
        return source; 
}

Я также добавил перегрузки для предиката индекса в метод расширения Where (). Для большего удовольствия добавьте вид, который включает дополнительный предикат else.

Мне это не очень нравится. Где уже выполняется if, в этом весь смысл предиката.

Greg D 19.07.2010 23:32

Отчасти идея заключается в том, что некоторые условные выражения не будут выполняться в SQL с IQueryable, поэтому вам придется их разделять.

andleer 23.07.2010 07:53

Встроенные преобразования: мне нравится этот небольшой шаблон. Завершено для Boolean, Double и DateTime. Разработан в соответствии с операторами C# является и как.

public static Int32? AsInt32(this string s)
{
    Int32 value;
    if (Int32.TryParse(s, out value))
        return value;

    return null;
}

public static bool IsInt32(this string s)
{
    return s.AsInt32().HasValue;
}

public static Int32 ToInt32(this string s)
{
    return Int32.Parse(s);
{

Мне нравится, но я бы не сказал «Есть» или «Как», потому что это подразумевает сравнение / преобразование типов. Это действительно метод разбора.

Greg 02.11.2009 21:24

Вы можете использовать ParseIsInt32 (), ParseToInt32 и ParseAsInt32 ()

andleer 03.11.2009 20:54

GetMemberName позволяет получить строку с именем члена с безопасностью времени компиляции.

public static string GetMemberName<T, TResult>(
    this T anyObject, 
    Expression<Func<T, TResult>> expression)
{
    return ((MemberExpression)expression.Body).Member.Name;
}

Использование:

"blah".GetMemberName(x => x.Length); // returns "Length"

Он поставляется вместе со статическим методом без расширения, если у вас нет экземпляра:

public static string GetMemberName<T, TReturn>(
    Expression<Func<T, TReturn>> expression)
    where T : class
{
    return ((MemberExpression)expression.Body).Member.Name;
}

Но звонок, конечно, выглядит не так красиво:

ReflectionUtility.GetMemberName((string) s => s.Length); // returns "Length"

Вы можете разместить его на Codeplex, если хотите.

public static bool In<T>(this T source, params T[] list)
{
  if (null==source) throw new ArgumentNullException("source");
  return list.Contains(source);
}

Позволяет заменить:

if (reallyLongIntegerVariableName == 1 || 
    reallyLongIntegerVariableName == 6 || 
    reallyLongIntegerVariableName == 9 || 
    reallyLongIntegerVariableName == 11)
{
  // do something....
}

and

if (reallyLongStringVariableName == "string1" || 
    reallyLongStringVariableName == "string2" || 
    reallyLongStringVariableName == "string3")
{
  // do something....
}

and

if (reallyLongMethodParameterName == SomeEnum.Value1 || 
    reallyLongMethodParameterName == SomeEnum.Value2 || 
    reallyLongMethodParameterName == SomeEnum.Value3 || 
    reallyLongMethodParameterName == SomeEnum.Value4)
{
  // do something....
}

С:

if (reallyLongIntegerVariableName.In(1,6,9,11))
{
      // do something....
}

and

if (reallyLongStringVariableName.In("string1","string2","string3"))
{
      // do something....
}

and

if (reallyLongMethodParameterName.In(SomeEnum.Value1, SomeEnum.Value2, SomeEnum.Value3, SomeEnum.Value4)
{
  // do something....
}

Сделал это тоже. Одна из тех редких вещей, которые мне не хватает в Visual Foxpro. :)

Arnis Lapsa 02.11.2009 21:59

Этот код не компилируется. У массива нет метода Contains

Ryu 18.11.2009 04:39

Он компилируется, если вы используете System.Linq;

Ryu 18.11.2009 04:48

Может быть, «EqualsAnyOf» было бы лучше, чем «In»?

Tom Bushell 20.01.2010 01:40

Не уверен, что мне это нравится - мне нравится краткость In, но, возможно, IsIn был бы лучше.

Winston Smith 20.01.2010 11:31

Используя тот же метод Contains: (new [] {1, 2, 3}). Contains (a)

Max Toro 05.02.2010 17:46

Ошибка Visual Studio: ошибка 2 'System.Array' не содержит определения для 'Contains', и не может быть найден метод расширения 'Contains', принимающий первый аргумент типа 'System.Array' (отсутствует ли у вас директива using или ссылка на сборку?)

KdgDev 12.07.2010 02:53

Проблема: для этого требуется более 1 параметра. В таком случае лучше всего использовать метод LINQ contains, верно?

KdgDev 12.07.2010 03:31

Я бы, наверное, написал два метода расширения - IsInOne () и IsInAll ()

BuddyJoe 05.08.2010 09:09

@webDevHobo - System.Linq является обязательным оператором using, также это означает, что у вас должен быть linqBridge.dll или .net 3.5 или выше. Содержит - msdn.microsoft.com/en-us/library/bb339118.aspx LinqBridge - code.google.com/p/linqbridge

Maslow 10.02.2011 00:39

Я тоже подумал о In<T>(...) и обнаружил, что это самый полезный метод расширения за пределами стандартной библиотеки. Но я не согласен с названием In. Предполагается, что имя метода описывает то, что он делает, но In этого не делает. Я назвал его IsAnyOf<T>(...), но думаю, IsIn<T>(...) тоже подойдет.

JBSnorro 19.08.2011 04:41

Название имеет смысл, особенно если вы использовали аналогичные функции в MySQL. dev.mysql.com/doc/refman/5.5/en/…

jeremysawesome 20.04.2012 20:47

Меня раздражало, что LINQ дает мне OrderBy, который принимает в качестве аргумента класс, реализующий IComparer, но не поддерживает передачу простой анонимной функции сравнения. Я исправил это.

Этот класс создает IComparer из вашей функции сравнения ...

/// <summary>
///     Creates an <see cref = "IComparer{T}"/> instance for the given
///     delegate function.
/// </summary>
internal class ComparerFactory<T> : IComparer<T>
{
    public static IComparer<T> Create(Func<T, T, int> comparison)
    {
        return new ComparerFactory<T>(comparison);
    }

    private readonly Func<T, T, int> _comparison;

    private ComparerFactory(Func<T, T, int> comparison)
    {
        _comparison = comparison;
    }

    #region IComparer<T> Members

    public int Compare(T x, T y)
    {
        return _comparison(x, y);
    }

    #endregion
}

... и эти методы расширения открывают мои новые перегрузки OrderBy для перечисляемых объектов. Я сомневаюсь, что это работает для LINQ to SQL, но отлично подходит для LINQ to Objects.

public static class EnumerableExtensions
{
    /// <summary>
    /// Sorts the elements of a sequence in ascending order by using a specified comparison delegate.
    /// </summary>
    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
                                                                     Func<TKey, TKey, int> comparison)
    {
        var comparer = ComparerFactory<TKey>.Create(comparison);
        return source.OrderBy(keySelector, comparer);
    }

    /// <summary>
    /// Sorts the elements of a sequence in descending order by using a specified comparison delegate.
    /// </summary>
    public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
                                                                               Func<TKey, TKey, int> comparison)
    {
        var comparer = ComparerFactory<TKey>.Create(comparison);
        return source.OrderByDescending(keySelector, comparer);
    }
}

Вы можете поместить это в codeplex, если хотите.

// Checks for an empty collection, and sends the value set in the default constructor for the desired field
public static TResult MinGuarded<T, TResult>(this IEnumerable<T> items, Func<T, TResult> expression) where T : new() {
    if (items.IsEmpty()) {
        return (new List<T> { new T() }).Min(expression);
    }
    return items.Min(expression);
}

// Checks for an empty collection, and sends the value set in the default constructor for the desired field
public static TResult MaxGuarded<T, TResult>(this IEnumerable<T> items, Func<T, TResult> expression) where T : new() {
    if (items.IsEmpty()) {
        return (new List<T> { new T() }).Max(expression);
    }
    return items.Max(expression);
}

Я не уверен, есть ли лучший способ сделать это, но это расширение очень полезно, когда я хочу иметь контроль над значениями полей по умолчанию в моем объекте. Например, если я хочу контролировать значение DateTime и хочу, чтобы оно было установлено в соответствии с моей бизнес-логикой, я могу сделать это в конструкторе по умолчанию. В противном случае получается DateTime.MinDate.

Я нашел это полезным

public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> pSeq)
{
    return pSeq ?? Enumerable.Empty<T>();
}

Он удаляет нулевую проверку в вызывающем коде. Теперь ты мог бы сделать

MyList.EmptyIfNull().Where(....)

Да, если кто-то забыл "Null Object Pattern", этот метод полезен для его исправления. Коллекция никогда не должна быть нулевой.

Pavel Hodek 20.07.2011 13:38

Несколько раз я обнаруживал, что мне нужно что-то вроде, я думаю, Groovy's "Safe navigation".

От http://groovy.codehaus.org/Statements:

If you are walking a complex object graph and don't want to have NullPointerExceptions thrown you can use the ?. operator rather than . to perform your navigation.

def foo = null def bar = foo?.something?.myMethod() assert bar == null

Итак, как вы думаете, стоит ли добавлять для него метод расширения? Что-то типа:

obj.SafelyNavigate(x => x.SomeProperty.MaybeAMethod().AnotherProperty);

Думаю, было бы неплохо, даже если бы это тоже могло доставить неприятности.

Если вы думаете, что это хорошая идея:

  • Как вы думаете, что должно произойти с типами значений ?, вернуть дефолт? throw ?, отключить его общим ограничением ?.
  • Проглатывание исключения NullReferenceException для его реализации было бы слишком рискованным ?, Что ты предлагаешь?, Ходить по дереву выражений, выполняя каждый вызов или доступ к члену, кажется трудным и своего рода излишеством (если это вообще возможно), не так ли?

Может быть, это просто плохая идея: D, но я считаю, что это может быть полезно, если все сделано правильно. Если ничего подобного нет, и вы думаете, что он имеет какую-то ценность, я могу попробовать и потом отредактировать ответ.

требует .NET 3.5, использует делегатов для оценки графа объекта в try catch stackoverflow.com/questions/298009/…

usman shaheen 18.07.2009 00:06

Я думаю, что это может быть хорошей идеей, но ее сложно реализовать ... Вы можете легко проверить, является ли x нулевым, но если вы хотите выполнить проверку для каждого уровня "пути" (SomeProperty, MaybeAMethod), он получит очень трудно. Я попытался сделать это, переписав дерево выражений, но в конце концов сдался ...

Thomas Levesque 08.02.2010 03:05

Это должен быть отдельный вопрос

Casebash 20.07.2010 08:56

В конце концов мне удалось заставить его работать: tomlev2.wordpress.com/2010/02/21/…

Thomas Levesque 19.08.2010 23:21

Эй, это здорово !. Я действительно попробовал это пару дней назад и придумал это: bit.ly/9777Y0, но ваш подход кажется намного чище. Но я согласен с вашими выводами.

Fredy Treboux 23.08.2010 07:04

Иногда удобно записать строку для выбранного элемента в списке с помощью специального разделителя.

Например, если у вас есть List<Person> и вы хотите зациклить фамилию, разделенную запятой, вы можете это сделать.

string result = string.Empty;
foreach (var person in personList) {
   result += person.LastName + ", ";
}
result = result.Substring(0, result.Length - 2);
return result;

Или вы можете использовать этот удобный метод расширения

public static string Join<T>(this IEnumerable<T> collection, Func<T, string> func, string separator)
{
  return String.Join(separator, collection.Select(func).ToArray());
}

И используйте это так

personList.Join(x => x.LastName, ", ");

Что дает тот же результат, в данном случае список фамилий, разделенных запятыми.

Я назвал свою версию этого ToDelimitedString, чтобы избежать путаницы со встроенным методом LINQ Join.

Joel Mueller 24.02.2010 02:29

Ниже приведен метод расширения, который адаптирует Код Рика Страла (и комментарии тоже), чтобы вам не приходилось угадывать или читать метку порядка байтов массива байтов или текстового файла каждый раз, когда вы конвертируете его в строку.

Этот фрагмент позволяет вам просто делать:

byte[] buffer = File.ReadAllBytes(@"C:\file.txt");
string content = buffer.GetString();

Если вы обнаружите какие-либо ошибки, пожалуйста, добавьте в комментарии. Не стесняйтесь включать его в проект Codeplex.

public static class Extensions
{
    /// <summary>
    /// Converts a byte array to a string, using its byte order mark to convert it to the right encoding.
    /// Original article: http://www.west-wind.com/WebLog/posts/197245.aspx
    /// </summary>
    /// <param name = "buffer">An array of bytes to convert</param>
    /// <returns>The byte as a string.</returns>
    public static string GetString(this byte[] buffer)
    {
        if (buffer == null || buffer.Length == 0)
            return "";

        // Ansi as default
        Encoding encoding = Encoding.Default;       

        /*
            EF BB BF    UTF-8 
            FF FE UTF-16    little endian 
            FE FF UTF-16    big endian 
            FF FE 00 00 UTF-32, little endian 
            00 00 FE FF UTF-32, big-endian 
         */

        if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
            encoding = Encoding.UTF8;
        else if (buffer[0] == 0xfe && buffer[1] == 0xff)
            encoding = Encoding.Unicode;
        else if (buffer[0] == 0xfe && buffer[1] == 0xff)
            encoding = Encoding.BigEndianUnicode; // utf-16be
        else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
            encoding = Encoding.UTF32;
        else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
            encoding = Encoding.UTF7;

        using (MemoryStream stream = new MemoryStream())
        {
            stream.Write(buffer, 0, buffer.Length);
            stream.Seek(0, SeekOrigin.Begin);
            using (StreamReader reader = new StreamReader(stream, encoding))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

Очень полезный метод, но я не думаю, что он должен быть методом расширения.

Pop Catalin 22.09.2009 19:11

Если вы пишете текстовый редактор, это, вероятно, требует использования метода расширения, но в большинстве случаев я согласен, что это, вероятно, не более чем статический частный метод.

Chris S 22.09.2009 22:39

Я всегда использую формат, который требует новой строки с StringBuilder, поэтому очень простое расширение, приведенное ниже, позволяет сэкономить несколько строк кода:

public static class Extensions
{
    public static void AppendLine(this StringBuilder builder,string format, params object[] args)
    {
        builder.AppendLine(string.Format(format, args));
    }
}

Альтернативой является AppendFormat в StringBuilder с \n или Environment.NewLine.

не стал бы ´builder.AppendFormat (format, args); builder.AppendLine (); ´ больше в духе конструктора строк (т.е. без временной строки)

adrianm 10.05.2010 14:09

@adrianm - это не еще одна лишняя строка (хотя и просто \ n)?

Chris S 10.05.2010 14:55

AppendLine использует статическую строку «\ r \ n». string.Format каждый раз создает новую строку, которая должна быть GC: d.

adrianm 11.05.2010 10:38

Двое маленьких (некоторые считают их глупыми), которых я вкладываю во все свои проекты:

public static bool IsNull(this object o){
  return o == null;
}

и

public static bool IsNullOrEmpty(this string s){
  return string.IsNullOrEmpty(s);
}

Это делает мой код намного более плавным ...

if (myClassInstance.IsNull()) //... do something

if (myString.IsNullOrEmpty()) //... do something

Я думаю, что это сделало бы действительно хорошие свойства расширения; если мы когда-нибудь их получим.

Было бы лучше? public static bool IsNull <T> (this T obj), где T: class {return (obj == null); }

Dan Diplo 16.11.2009 18:12
@Dan Diplo Я не думаю, что ваше изменение вообще что-то изменит. Нет никакой разницы в использовании дженериков для сравнения объекта с нулевым значением.
jpbochi 12.01.2010 19:11

Преимущество использования дженериков заключается в следующем: если вы попытаетесь вызвать общую версию IsNull () Дэна Дипло для структуры, вы получите ошибку времени компиляции. Если вы вызываете оригинальную версию Джона Крафта, она не предупреждает вас (а также добавляет бокс-код).

Ray Burns 04.02.2010 04:23

@Ray Burns: Вы абсолютно правы, однако я считаю это плюсом для себя; он не вызовет исключения, и упаковка является предпочтительным поведением в коде, который я пишу. Кроме того, в текущей среде, в которой я работаю (бизнес-приложения CRUD), нам никогда не нужны структуры; так что это не имеет значения.

John Kraft 05.02.2010 01:35

Это не плюс, потому что он всегда возвращает false для структур. Это означает, что если кто-то использует его в структуре, скорее всего, он сделал что-то по ошибке, но не получит ни одной ошибки или предупреждения. Учитывая, что метод Дэна выглядит идентично, когда вы его вызываете (т. Е. Нет недостатков в его использовании), то вы никак не можете считать свою версию плюсом.

MikeKulls 19.08.2011 11:18

Меня смущает беспокойство по поводу структур. Структура по определению никогда не может быть нулевой. Поэтому тот факт, что он возвращает false для структуры, является правильным поведением.

John Kraft 19.08.2011 17:20

+1, я использую их все время. Я как раз собирался отправить ответ с тем же кодом точно, пока не обнаружил, что ваш ответ уже существует: D

demoncodemonkey 11.02.2012 03:02

Я использую их в своих проектах Silverlight:

public static void Show(this UIElement element)
{
    element.Visibility = Visibility.Visible;
}

public static void Hide(this UIElement element)
{
    element.Visibility = Visibility.Collapsed;
}

Уменьшает длину строки до toLength и добавляет дополнительную строку в конец сокращенной строки, чтобы обозначить, что строка была сокращена (по умолчанию ...)

public static string Shorten(this string str, int toLength, string cutOffReplacement = " ...")
{
    if (string.IsNullOrEmpty(str) || str.Length <= toLength)
        return str;
    else
        return str.Remove(toLength) + cutOffReplacement;
}

Добавление "..." вернет ShortenToLength + 4 символа, то есть строку длиннее, чем указано в документации.

sisve 02.11.2009 21:50

@Simon - Исправлена ​​документация. @peSHIr - Я редактировал, не могли бы вы уточнить.

Omar 05.07.2010 20:53

Удобный способ разобраться с размерами:

public static class Extensions {
    public static int K(this int value) {
        return value * 1024;
    }
    public static int M(this int value) {
        return value * 1024 * 1024;
    }
}

public class Program {
    public void Main() {
        WSHttpContextBinding serviceMultipleTokenBinding = new WSHttpContextBinding() {
            MaxBufferPoolSize = 2.M(), // instead of 2097152
            MaxReceivedMessageSize = 64.K(), // instead of 65536
        };
    }
}

На мой взгляд, это действительно плохой стиль кодирования. Вместо этого следует использовать константы, а не запутанную логику.

xxbbcc 20.02.2012 10:39

FindControl со встроенным кастингом:

public static T FindControl<T>(this Control control, string id) where T : Control
{
    return (T)control.FindControl(id);
}

В этом нет ничего удивительного, но я чувствую, что это делает код более чистым.

// With extension method
container.FindControl<TextBox>("myTextBox").SelectedValue = "Hello world!";

// Without extension method
((TextBox)container.FindControl("myTextBox")).SelectedValue = "Hello world!";

При желании это можно поместить в проект codeplex.

Я бы предпочел использовать as T вместо литья

jpbochi 12.01.2010 18:55

Шаблон для синтаксического анализа, который избегает параметров out:

public static bool TryParseInt32(this string input, Action<int> action)
{
    int result;
    if (Int32.TryParse(input, out result))
    {
        action(result);
        return true;
    }
    return false;
}

Использование:

if (!textBox.Text.TryParseInt32(number => label.Text = SomeMathFunction(number)))
    label.Text = "Please enter a valid integer";

При желании это можно поместить в проект codeplex.

В ASP.NET мне всегда надоедает использование FindControl, а затем необходимость приводить и проверять, является ли значение нулевым перед ссылкой. Итак, я добавил метод TryParse () в Контроль, который отражает аналогичные методы в структуре для Int32 и т. д.

public static bool TryParse<T>(this Control control, string id, out T result) 
    where T : Control
{
    result = control.FindControl(id) as T;
    return result != null;
}

Итак, теперь вы можете сделать это на страницах веб-форм ASP.NET:

Label lbl;
if (Page.TryParse("Label1", out lbl))
{
    lbl.Text = "Safely set text";
}

Я считаю это довольно полезным:

public static class PaulaBean
{
    private static String paula = "Brillant";
    public static String GetPaula<T>(this T obj) {
        return paula;
    }
}

Вы можете использовать его на CodePlex.

Может ли кто-нибудь быть достаточно любезным, чтобы объяснить это менее одаренным из нас?

jpbochi 07.01.2010 00:06

хахаха. Просто прочтите статью (комментарий Джоэла выше) - забавно, правда, но, будучи почти в той же лодке (в конце приема, а не в конце Паулы), это только забавно, оглядываясь назад! Однажды к работе над проектом, в котором я работал дизайнером / ведущим разработчиком, был привлечен подрядчик - она ​​не находилась под моим непосредственным контролем, но ей была поручена работа из рабочего списка моей команды. Боссы хвалили ее как блестящую (даже позже наняли ее снова в качестве руководителя разработки!). Они никогда не догадывались, что каждый фрагмент кода, который она написала или спроектировал, не попал в производство, и все должны были быть полностью переписаны моей командой с нуля!

Wolf5370 02.05.2012 10:14

Бинарный поиск:

public static T BinarySearch<T, TKey>(this IList<T> list, Func<T, TKey> keySelector, TKey key)
        where TKey : IComparable<TKey>
{
    int min = 0;
    int max = list.Count;
    int index = 0;
    while (min < max)
    {
        int mid = (max + min) / 2;
        T midItem = list[mid];
        TKey midKey = keySelector(midItem);
        int comp = midKey.CompareTo(key);
        if (comp < 0)
        {
            min = mid + 1;
        }
        else if (comp > 0)
        {
            max = mid - 1;
        }
        else
        {
            return midItem;
        }
    }
    if (min == max &&
        keySelector(list[min]).CompareTo(key) == 0)
    {
        return list[min];
    }
    throw new InvalidOperationException("Item not found");
}

Использование (при условии, что список отсортирован по Id):

var item = list.BinarySearch(i => i.Id, 42);

Тот факт, что он генерирует исключение InvalidOperationException, может показаться странным, но именно это делает Enumerable.First, когда нет подходящего элемента.

Это может быть весьма полезно:

    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector)
    {
        if (first == null)
            throw new ArgumentNullException("first");
        if (second == null)
            throw new ArgumentNullException("second");
        if (selector == null)
            throw new ArgumentNullException("selector");

        using (var enum1 = first.GetEnumerator())
        using (var enum2 = second.GetEnumerator())
        {
            while (enum1.MoveNext() && enum2.MoveNext())
            {
                yield return selector(enum1.Current, enum2.Current);
            }
        }
    }

Он был добавлен в класс Enumerable в .NET 4.0, но удобно иметь его в 3.5.

Пример :

var names = new[] { "Joe", "Jane", "Jack", "John" };
var ages = new[] { 42, 22, 18, 33 };

var persons = names.Zip(ages, (n, a) => new { Name = n, Age = a });

foreach (var p in persons)
{
    Console.WriteLine("{0} is {1} years old", p.Name, p.Age);
}

Помните, что проверка аргументов не должна выполняться в том же методе, что и yield return. Проблема в том, что ArgumentNullException будет брошен в начале итерации возвращенного «запроса». Его надо было выбросить при вызове метода Zip.

jpbochi 07.01.2010 20:03

Ой, почему бы и нет! Вот расширение IList (не может быть IEnumerable, потому что я использую специальные функции списка) для сортировки вставкой.

internal static class SortingHelpers
{
    /// <summary>
    /// Performs an insertion sort on this list.
    /// </summary>
    /// <typeparam name = "T">The type of the list supplied.</typeparam>
    /// <param name = "list">the list to sort.</param>
    /// <param name = "comparison">the method for comparison of two elements.</param>
    /// <returns></returns>
    public static void InsertionSort<T>(this IList<T> list, Comparison<T> comparison)
    {
        for (int i = 2; i < list.Count; i++)
        {
            for (int j = i; j > 1 && comparison(list[j], list[j - 1]) < 0; j--)
            {
                T tempItem = list[j];
                list.RemoveAt(j);
                list.Insert(j - 1, tempItem);
            }
        }
    }
}

Пример:

List<int> list1 = { 3, 5, 1, 2, 9, 4, 6 };
list1.InsertionSort((a,b) => a - b);
//list is now in order of 1,2,3,4,5,6,9

Нет примера? Это довольно просто, но, возможно, не для всех

Thomas Levesque 20.11.2009 21:43

Было бы больше .NETy, если бы вы использовали IComparer<T> или Comparison<T> (или перегрузку для каждого) вместо функции «меньше», которая попахивает STL.

P Daddy 23.11.2009 23:09

Насколько я понимаю, Comparison <T> - это в основном замаскированный Func <T, T, int>, где мне нужен bool для работы с сортировкой вставки.

RCIX 24.11.2009 02:23

Да, Comparison<T> эквивалентен Func<T, T, int>, который имеет тот же интерфейс, что и IComparer<T>.Compare. Это стандартный интерфейс компаратора, к которому привыкли разработчики .NET. Большинству функций сортировки нужно сравнивать только меньше или больше. Вы выбрали меньше. Если вы посмотрите в Reflector на Array.SorterGenericArray.QuickSort() (или Array.SorterObjectArray.QuickSort()), вы увидите, что Array.Sort также использует только меньше, но делает это с comparer.Compare(a, b) < 0, сохраняя установленный интерфейс для платформы.

P Daddy 24.11.2009 03:56

Принятие IComparer<T> также позволяет вашим пользователям (или вам) использовать Comparer<T>.Default вместо того, чтобы выполнять сравнение вручную. Лучший интерфейс при сравнении обычно имеет три перегрузки: одна принимает IComparer<T>, другая - Comparison<T>, а третья не использует компаратор и предполагает Comparer<T>.Default.

P Daddy 24.11.2009 04:00

В разделе недавних поисков на странице статистики моего блога я удалил все дубликаты, но мне нужен был способ удалить почти повторяющиеся строки. Я бы получил массу похожих, но не совсем одинаковых запросов Google.

В итоге я использовал анонимный тип вместо словаря, но мне нужен был способ создать список этого анонимного типа. Вы не можете этого сделать, но вы можете создать List<dynamic> в .NET 4.0 :)

В основном мне это нравится, потому что я фактически получаю List<AnonymousType#1>().

/// <summary>Remove extraneous entries for common word permutations</summary>
/// <param name = "input">Incoming series of words to be filtered</param>
/// <param name = "MaxIgnoreLength">Words this long or shorter will not count as duplicates</param>
/// <param name = "words2">Instance list from BuildInstanceList()</param>
/// <returns>Filtered list of lines from input, based on filter info in words2</returns>
private static List<string> FilterNearDuplicates(List<string> input, int MaxIgnoreLength, List<dynamic> words2)
{
    List<string> output = new List<string>();
    foreach (string line in input)
    {
        int Dupes = 0;
        foreach (string word in line.Split(new char[] { ' ', ',', ';', '\\', '/', ':', '\"', '\r', '\n', '.' })
            .Where(p => p.Length > MaxIgnoreLength)
            .Distinct())
        {
            int Instances = 0;
            foreach (dynamic dyn in words2)
            if (word == dyn.Word)
            {
                Instances = dyn.Instances;
                if (Instances > 1)
                    Dupes++;
                break;
            }
        }
        if (Dupes == 0)
            output.Add(line);
    }
    return output;
}
/// <summary>Builds a list of words and how many times they occur in the overall list</summary>
/// <param name = "input">Incoming series of words to be counted</param>
/// <returns></returns>
private static List<dynamic> BuildInstanceList(List<string> input)
{
    List<dynamic> words2 = new List<object>();
    foreach (string line in input)
    foreach (string word in line.Split(new char[] { ' ', ',', ';', '\\', '/', ':', '\"', '\r', '\n', '.' }))
    {
        if (string.IsNullOrEmpty(word))
            continue;
        else if (ExistsInList(word, words2))
            for (int i = words2.Count - 1; i >= 0; i--)
            {
                if (words2[i].Word == word)
                    words2[i] = new { Word = words2[i].Word, Instances = words2[i].Instances + 1 };
            }
        else
            words2.Add(new { Word = word, Instances = 1 });
    }

    return words2;
}
/// <summary>Determines whether a dynamic Word object exists in a List of this dynamic type.</summary>
/// <param name = "word">Word to look for</param>
/// <param name = "words">Word dynamics to search through</param>
/// <returns>Indicator of whether the word exists in the list of words</returns>
private static bool ExistsInList(string word, List<dynamic> words)
{
    foreach (dynamic dyn in words)
        if (dyn.Word == word)
            return true;
    return false;
}

Оборачивает строку через каждые n символов.

public static string WrapAt(this string str, int WrapPos)
{
    if (string.IsNullOrEmpty(str))
        throw new ArgumentNullException("str", "Cannot wrap a null string");
    str = str.Replace("\r", "").Replace("\n", "");

    if (str.Length <= WrapPos)
        return str;

    for (int i = str.Length; i >= 0; i--)
        if (i % WrapPos == 0 && i > 0 && i != str.Length)
            str = str.Insert(i, "\r\n");
    return str;
}

Получает корневой домен URI.

/// <summary>Gets the root domain of any URI</summary>
/// <param name = "uri">URI to get root domain of</param>
/// <returns>Root domain with TLD</returns>
public static string GetRootDomain(this System.Uri uri)
{
    if (uri == null)
        return null;

    string Domain = uri.Host;
    while (System.Text.RegularExpressions.Regex.Matches(Domain, @"[\.]").Count > 1)
        Domain = Domain.Substring(Domain.IndexOf('.') + 1);
    Domain = Domain.Substring(0, Domain.IndexOf('.'));
    return Domain;
}

Это кажется немного неправильным в предположении структуры FQDN - попробуйте эти тестовые примеры. www.msn.co.uk, www.msn.com и msn.co.uk co определенно не является корневым доменом для msn.co.uk.

stephbu 16.05.2010 21:16

Некоторые расширения DataSet / DataRow для упрощения работы с результатами базы данных

Просто используйте .Field ("fieldname") в DataRow, и он будет использовать его, если это возможно, можно указать необязательное значение по умолчанию.

Также .HasRows () в DataSet, поэтому вам не нужно проверять наличие таблицы и строк.

Пример:

using (DataSet ds = yourcall()) 
{
  if (ds.HasRows())
  {
     foreach (DataRow dr in ds.Tables[0].Rows)
     {
        int id = dr.Field<int>("ID");
        string name = dr.Field<string>("Name");
        string Action = dr.Field<string>("Action", "N/A");
     }
  }
}

Код:

using System;
using System.Data;

public static class DataSetExtensions
{
    public static T Field<T>(this DataRow row, string columnName, T defaultValue)
    {
        try
        {
            return row.Field<T>(columnName);
        }
        catch
        {
            return defaultValue;
        }
    }

    public static T Field<T>(this DataRow row, string columnName)
    {
        if (row[columnName] == null)
            throw new NullReferenceException(columnName + " does not exist in DataRow");

        string value = row[columnName].ToString();

        if (typeof(T) == "".GetType())
        {
            return (T)Convert.ChangeType(value, typeof(T));
        }
        else if (typeof(T) == 0.GetType())
        {
            return (T)Convert.ChangeType(int.Parse(value), typeof(T));
        }
        else if (typeof(T) == false.GetType())
        {
            return (T)Convert.ChangeType(bool.Parse(value), typeof(T));
        }
        else if (typeof(T) == DateTime.Now.GetType())
        {
            return (T)Convert.ChangeType(DateTime.Parse(value), typeof(T));
        }
        else if (typeof(T) == new byte().GetType())
        {
            return (T)Convert.ChangeType(byte.Parse(value), typeof(T));
        }
        else if (typeof(T) == new float().GetType())
        {
            return (T)Convert.ChangeType(float.Parse(value), typeof(T));
        }
        else
        {
            throw new ArgumentException(string.Format("Cannot cast '{0}' to '{1}'.", value, typeof(T).ToString()));
        }
    }

    public static bool HasRows(this DataSet dataSet) 
    {
        return (dataSet.Tables.Count > 0 && dataSet.Tables[0].Rows.Count > 0);
    }
}

Очень похоже на System.Data.DataSetExtensions.dll, который поставляется с .NET 3.5, но не так эффективен.

Joel Mueller 24.02.2010 02:37

Некоторые удобные помощники по строкам:

Использование:

Я ненавижу нежелательные пробелы в конце или в начале строки, и поскольку строка может принимать значение null, это может быть сложно, поэтому я использую это:

public bool IsGroup { get { return !this.GroupName.IsNullOrTrimEmpty(); } }

Вот еще один метод расширения, который я использую для нового структура валидации, который я тестирую. Вы можете увидеть расширения регулярных выражений, которые помогают очистить регулярное выражение, которое в противном случае было бы беспорядочным:

public static bool IsRequiredWithLengthLessThanOrEqualNoSpecial(this String str, int length)
{
    return !str.IsNullOrTrimEmpty() &&
        str.RegexMatch(
            @"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,{0}}$".RegexReplace(@"\{0\}", length.ToString()),
            RegexOptions.Multiline) == str;
}

Источник:

public static class StringHelpers
{
    /// <summary>
    /// Same as String.IsNullOrEmpty except that
    /// it captures the Empty state for whitespace
    /// strings by Trimming first.
    /// </summary>
    public static bool IsNullOrTrimEmpty(this String helper)
    {
        if (helper == null)
            return true;
        else
            return String.Empty == helper.Trim();
    }

    public static int TrimLength(this String helper)
    {
        return helper.Trim().Length;
    }

    /// <summary>
    /// Returns the matched string from the regex pattern. The
    /// groupName is for named group match values in the form (?<name>group).
    /// </summary>
    public static string RegexMatch(this String helper, string pattern, RegexOptions options, string groupName)
    {
        if (groupName.IsNullOrTrimEmpty())
            return Regex.Match(helper, pattern, options).Value;
        else
            return Regex.Match(helper, pattern, options).Groups[groupName].Value;
    }

    public static string RegexMatch(this String helper, string pattern)
    {
        return RegexMatch(helper, pattern, RegexOptions.None, null);
    }

    public static string RegexMatch(this String helper, string pattern, RegexOptions options)
    {
        return RegexMatch(helper, pattern, options, null);
    }

    public static string RegexMatch(this String helper, string pattern, string groupName)
    {
        return RegexMatch(helper, pattern, RegexOptions.None, groupName);
    }

    /// <summary>
    /// Returns true if there is a match from the regex pattern
    /// </summary>
    public static bool IsRegexMatch(this String helper, string pattern, RegexOptions options)
    {
        return helper.RegexMatch(pattern, options).Length > 0;
    }

    public static bool IsRegexMatch(this String helper, string pattern)
    {
        return helper.IsRegexMatch(pattern, RegexOptions.None);
    }

    /// <summary>
    /// Returns a string where matching patterns are replaced by the replacement string.
    /// </summary>
    /// <param name = "pattern">The regex pattern for matching the items to be replaced</param>
    /// <param name = "replacement">The string to replace matching items</param>
    /// <returns></returns>
    public static string RegexReplace(this String helper, string pattern, string replacement, RegexOptions options)
    {
        return Regex.Replace(helper, pattern, replacement, options);
    }

    public static string RegexReplace(this String helper, string pattern, string replacement)
    {
        return Regex.Replace(helper, pattern, replacement, RegexOptions.None);
    }
}

Мне нравится делать много регулярных выражений, поэтому я считаю это проще, чем добавлять оператор using и дополнительный код для обработки именованных групп.

Ваш помощник IsNullOrTrimEmpty находится в .NET 4.0 в виде строки.

ICR 12.12.2009 11:05

Ребята из BCL, но да, привет 5 действительно.

ICR 12.12.2009 11:23

Я реализовал пакет методов расширения (доступных по адресу http://foop.codeplex.com/), и некоторые из моих повседневных методов:

// the most beloved extension method for me is Pipe:
<%= variable.Pipe(x => this.SomeFunction(x)).Pipe(y =>
{
    ...;
    return this.SomeOtherFunction(y);
}) %>

var d = 28.December(2009); // some extension methods for creating DateTime
DateTime justDatePart = d.JustDate();
TimeSpan justTimePart = d.JustTime();
var nextTime = d.Add(5.Hours());

using(StreamReader reader = new StreamReader("lines-of-data-file-for-example")) {
    ...
    // for reading streams line by line and usable in LINQ
    var query = from line in reader.Lines(); 
                where line.Contains(_today)
                select new { Parts = PartsOf(line), Time = _now };
}

500.Sleep();

XmlSerialize and XmlDeserialize

IsNull and IsNotNull

IfTrue, IfFalse and Iff:
true.IfTrue(() => Console.WriteLine("it is true then!");

IfNull and IfNotNull

+1 Мне особенно понравился метод Pipe. Однако мне пришлось загрузить ваш исходный код, чтобы узнать, что это похоже на Where с одним значением. Может ли кто-нибудь отредактировать ответ, чтобы прояснить этот момент?

jpbochi 12.01.2010 18:46

«Труба» отличается от «Где». «Где» - это «карта» (в математическом смысле и на функциональных языках), которая принимает набор (коллекция и в случае .NET IEnumerable <T>) и функцию (которая в .NET-мире является делегатом и может быть представлена с помощью лямбда-выражения, такого как x => x> 2; единственное ограничение для предоставленного предиката состоит в том, что он должен возвращать логическое значение. Оператор «конвейер» - распространенный инструмент в функциональных языках. Его основное использование - для объединения вычислений (вызовов функций). Он получает значение и функцию (например, x => f (x)); затем он применяет функцию к значению и возвращает результат.

Kaveh Shahbazian 14.01.2010 01:44

Боюсь, что некоторые из них мне не нравятся. Например, 500.Sleep () ... На мой взгляд, это слишком загадочно. Я не вижу, что не так с обычным Thread.Sleep ()

Ian 28.01.2010 12:14

Для ASP.NET я использую эти расширения HttpSessionState для загрузки объектов в сеансе. Он позволяет загружать объекты сеанса в чистом виде и создает и инициализирует их, если они не существуют. Я использую два таких метода расширения:

private bool CreateMode;
private MyClass SomeClass;

protected override void OnInit (EventArgs e)
{
    CreateMode = Session.GetSessionValue<bool> ("someKey1", () => true);
    SomeClass = Session.GetSessionClass<MyClass> ("someKey2", () => new MyClass () 
    { 
       MyProperty = 123 
    });
}

Вот классы расширения:

public static class SessionExtensions    
{
    public delegate object UponCreate ();
    public static T GetSessionClass<T> (this HttpSessionState session, 
       string key, UponCreate uponCreate) where T : class
    {
        if (null == session[key])
        {
            var item = uponCreate () as T;
            session[key] = item;
            return item;
        }
        return session[key] as T;
    }
    public static T GetSessionValue<T> (this HttpSessionState session, 
       string key, UponCreate uponCreate) where T : struct
    {
        if (null == session[key])
        {
            var item = uponCreate();
            session[key] = item;
            return (T)item;
        }
        return (T)session[key];
    }
}

Используйте, как хотите, для CodePlex или для подкладки в клетку для птиц.

moomi 28.12.2009 17:19

IEnumerable<> в случайном порядке

Я использовал алгоритм Фишер-Йейтс для реализации функции перемешивания.

Используя yield return и разбивая код на две функции, он достигает правильных проверка аргумента и отсроченное исполнение. (спасибо, Дэн, за указание на этот недостаток в моей первой версии)

static public IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
    if (source == null) throw new ArgumentNullException("source");

    return ShuffleIterator(source);
}

static private IEnumerable<T> ShuffleIterator<T>(this IEnumerable<T> source)
{
    T[] array = source.ToArray();
    Random rnd = new Random();          
    for (int n = array.Length; n > 1;)
    {
        int k = rnd.Next(n--); // 0 <= k < n

        //Swap items
        if (n != k)
        {
            T tmp = array[k];
            array[k] = array[n];
            array[n] = tmp;
        }
    }

    foreach (var item in array) yield return item;
}

Если вы намерены использовать этот метод в запросах LINQ, вы можете рассмотреть возможность реализации класса ShuffledEnumerable, который выполняет эту работу (и, вероятно, кэширует ее) только на GetEnumerator, чтобы обеспечить ленивую оценку, известную как отложенное выполнение. В противном случае, если кто-то позвонит, например, var shuffledNames = myObjects.Select(x => x.Name).Distinct().Shuffle();, операция будет выполнена немедленно, что может быть не тем, чего он / она ожидает. Но хороший ответ!

Dan Tao 13.03.2010 21:42

@Dan: Это отличный момент. Однако есть элегантный способ использовать отложенное выполнение без явного объявления класса. yield return решает проблему. Отредактирую свой ответ.

jpbochi 15.03.2010 04:30

Твердый. Теперь это в основном логическая противоположность OrderBy. Отлично сделано!

Dan Tao 15.03.2010 15:30

Только что нашел здесь немного более гибкую версию: stackoverflow.com/a/5807238/123897

jpbochi 06.01.2012 21:15

Вы должны удалить цикл foreach и заменить тело оператора if на yield return array[k] = array[n];.

leviathanbadger 21.03.2012 05:49

Работая с MVC и имея множество операторов if, где меня интересуют только true или false, и печать null или string.Empty в другом случае, я придумал:

public static TResult WhenTrue<TResult>(this Boolean value, Func<TResult> expression)
{
    return value ? expression() : default(TResult);
}

public static TResult WhenTrue<TResult>(this Boolean value, TResult content)
{
    return value ? content : default(TResult);
}

public static TResult WhenFalse<TResult>(this Boolean value, Func<TResult> expression)
{
    return !value ? expression() : default(TResult);
}

public static TResult WhenFalse<TResult>(this Boolean value, TResult content)
{
    return !value ? content : default(TResult);
}

Это позволяет мне заменить <%= (someBool) ? "print y" : string.Empty %> на <%= someBool.WhenTrue("print y") %>.

Я использую его только в своих представлениях, где я смешиваю код и HTML, в файлах кода писать «более длинную» версию более понятно ИМХО.

Вы все, вероятно, уже знаете, что интересное использование методов расширения - это вид миксина. Некоторые методы расширения, такие как XmlSerializable, загрязняют почти каждый класс; и это не имеет смысла для большинства из них, таких как Thread и SqlConnection.

Некоторая функциональность должна быть добавлена ​​явно к классам, которые хотят ее иметь. Я предлагаю этому типу новые обозначения с префиксом M.

Итак, XmlSerializable выглядит следующим образом:

public interface MXmlSerializable { }
public static class XmlSerializable {
  public static string ToXml(this MXmlSerializable self) {
    if (self == null) throw new ArgumentNullException();
    var serializer = new XmlSerializer(self.GetType());
    using (var writer = new StringWriter()) {
      serializer.Serialize(writer, self);
      return writer.GetStringBuilder().ToString();
    }
  }
  public static T FromXml<T>(string xml) where T : MXmlSerializable {
    var serializer = new XmlSerializer(typeof(T));
    return (T)serializer.Deserialize(new StringReader(xml));
  }
}

Затем класс смешивает это:

public class Customer : MXmlSerializable {
  public string Name { get; set; }
  public bool Preferred { get; set; }
}

И использование просто:

var customer = new Customer { 
  Name = "Guybrush Threepwood", 
  Preferred = true };
var xml = customer.ToXml();

Если вам нравится идея, вы можете создать новое пространство имен для полезных миксинов в проекте. Что вы думаете?

Да, кстати, я думаю, что большинство методов расширения должны использовать явно проверить на нуль.

public static class StringHelper
{
    public static String F(this String str, params object[] args)
    {
        return String.Format(str, args);
    }
}

Используя как:

"Say {0}".F("Hello");

String.As<T>, который можно использовать для преобразования строкового значения как некоторого типа (предназначенного для использования в основном с примитивами и типами, поддерживающими IConvertable. Отлично работает с типами Nullable и даже с перечислениями!

public static partial class StringExtensions
{
    /// <summary>
    /// Converts the string to the specified type, using the default value configured for the type.
    /// </summary>
    /// <typeparam name = "T">Type the string will be converted to. The type must implement IConvertable.</typeparam>
    /// <param name = "original">The original string.</param>
    /// <returns>The converted value.</returns>
    public static T As<T>(this String original)
    {
        return As(original, CultureInfo.CurrentCulture,
                  default(T));
    }

    /// <summary>
    /// Converts the string to the specified type, using the default value configured for the type.
    /// </summary>
    /// <typeparam name = "T">Type the string will be converted to.</typeparam>
    /// <param name = "original">The original string.</param>
    /// <param name = "defaultValue">The default value to use in case the original string is null or empty, or can't be converted.</param>
    /// <returns>The converted value.</returns>
    public static T As<T>(this String original, T defaultValue)
    {
        return As(original, CultureInfo.CurrentCulture, defaultValue);
    }

    /// <summary>
    /// Converts the string to the specified type, using the default value configured for the type.
    /// </summary>
    /// <typeparam name = "T">Type the string will be converted to.</typeparam>
    /// <param name = "original">The original string.</param>
    /// <param name = "provider">Format provider used during the type conversion.</param>
    /// <returns>The converted value.</returns>
    public static T As<T>(this String original, IFormatProvider provider)
    {
        return As(original, provider, default(T));
    }

    /// <summary>
    /// Converts the string to the specified type.
    /// </summary>
    /// <typeparam name = "T">Type the string will be converted to.</typeparam>
    /// <param name = "original">The original string.</param>
    /// <param name = "provider">Format provider used during the type conversion.</param>
    /// <param name = "defaultValue">The default value to use in case the original string is null or empty, or can't be converted.</param>
    /// <returns>The converted value.</returns>
    /// <remarks>
    /// If an error occurs while converting the specified value to the requested type, the exception is caught and the default is returned. It is strongly recommended you
    /// do NOT use this method if it is important that conversion failures are not swallowed up.
    ///
    /// This method is intended to be used to convert string values to primatives, not for parsing, converting, or deserializing complex types.
    /// </remarks>
    public static T As<T>(this String original, IFormatProvider provider,
                          T defaultValue)
    {
        T result;
        Type type = typeof (T);

        if (String.IsNullOrEmpty(original)) result = defaultValue;
        else
        {
            // need to get the underlying type if T is Nullable<>.

            if (type.IsNullableType())
            {
                type = Nullable.GetUnderlyingType(type);
            }

            try
            {
                // ChangeType doesn't work properly on Enums
                result = type.IsEnum
                             ? (T) Enum.Parse(type, original, true)
                             : (T) Convert.ChangeType(original, type, provider);
            }
            catch // HACK: what can we do to minimize or avoid raising exceptions as part of normal operation? custom string parsing (regex?) for well-known types? it would be best to know if you can convert to the desired type before you attempt to do so.
            {
                result = defaultValue;
            }
        }

        return result;
    }
}

Это зависит от другого простого расширения для Type:

/// <summary>
/// Extension methods for <see cref = "Type"/>.
/// </summary>
public static class TypeExtensions
{
    /// <summary>
    /// Returns whether or not the specified type is <see cref = "Nullable{T}"/>.
    /// </summary>
    /// <param name = "type">A <see cref = "Type"/>.</param>
    /// <returns>True if the specified type is <see cref = "Nullable{T}"/>; otherwise, false.</returns>
    /// <remarks>Use <see cref = "Nullable.GetUnderlyingType"/> to access the underlying type.</remarks>
    public static bool IsNullableType(this Type type)
    {
        if (type == null) throw new ArgumentNullException("type");

        return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof (Nullable<>));
    }
}

Использование:

var someInt = "1".As<int>();
var someIntDefault = "bad value".As(1); // "bad value" won't convert, so the default value 1 is returned.
var someEnum = "Sunday".As<DayOfWeek>();
someEnum = "0".As<DayOfWeek>(); // returns Sunday
var someNullableEnum = "".As<DayOfWeek?>(null); // returns a null value since "" can't be converted

Ненавидите такой код?

CloneableClass cc1 = new CloneableClass ();
CloneableClass cc2 = null;
CloneableClass cc3 = null;

cc3 = (CloneableClass) cc1.Clone (); // this is ok
cc3 = cc2.Clone (); // this throws null ref exception
// code to handle both cases
cc3 = cc1 != null ? (CloneableClass) cc1.Clone () : null;

Это немного неуклюже, поэтому я заменяю его этим расширением, которое я называю CloneOrNull -

public static T CloneOrNull<T> (this T self) where T : class, ICloneable
{
    if (self == null) return null;
    return (T) self.Clone ();
}

Использование похоже:

CloneableClass cc1 = new CloneableClass ();
CloneableClass cc2 = null;
CloneableClass cc3 = null;

cc3 = cc1.CloneOrNull (); // clone of cc1
cc3 = cc2.CloneOrNull (); // null
// look mom, no casts!

Пожалуйста, не стесняйтесь использовать это где угодно!

Класс Random предоставляет множество функциональных возможностей.

Ниже приведены некоторые методы расширения, которые я время от времени использую. С ними, в дополнение к Next и NextDouble, класс Random дает вам NextBool, NextChar, NextDateTime, NextTimeSpan, NextDouble (принимает параметры minValue и maxValue) и мой любимый личный: NextString. Есть и другие (NextByte, NextShort, NextLong и др.); но они в основном предназначены для полноты и редко используются. Поэтому я не включил их сюда (этот код и так достаточно длинный!).

// todo: implement additional CharType values (e.g., AsciiAny)
public enum CharType {
    AlphabeticLower,
    AlphabeticUpper,
    AlphabeticAny,
    AlphanumericLower,
    AlphanumericUpper,
    AlphanumericAny,
    Numeric
}

public static class RandomExtensions {
    // 10 digits vs. 52 alphabetic characters (upper & lower);
    // probability of being numeric: 10 / 62 = 0.1612903225806452
    private const double AlphanumericProbabilityNumericAny = 10.0 / 62.0;

    // 10 digits vs. 26 alphabetic characters (upper OR lower);
    // probability of being numeric: 10 / 36 = 0.2777777777777778
    private const double AlphanumericProbabilityNumericCased = 10.0 / 36.0;

    public static bool NextBool(this Random random, double probability) {
        return random.NextDouble() <= probability;
    }

    public static bool NextBool(this Random random) {
        return random.NextDouble() <= 0.5;
    }

    public static char NextChar(this Random random, CharType mode) {
        switch (mode) {
            case CharType.AlphabeticAny:
                return random.NextAlphabeticChar();
            case CharType.AlphabeticLower:
                return random.NextAlphabeticChar(false);
            case CharType.AlphabeticUpper:
                return random.NextAlphabeticChar(true);
            case CharType.AlphanumericAny:
                return random.NextAlphanumericChar();
            case CharType.AlphanumericLower:
                return random.NextAlphanumericChar(false);
            case CharType.AlphanumericUpper:
                return random.NextAlphanumericChar(true);
            case CharType.Numeric:
                return random.NextNumericChar();
            default:
                return random.NextAlphanumericChar();
        }
    }

    public static char NextChar(this Random random) {
        return random.NextChar(CharType.AlphanumericAny);
    }

    private static char NextAlphanumericChar(this Random random, bool uppercase) {
        bool numeric = random.NextBool(AlphanumericProbabilityNumericCased);

        if (numeric)
            return random.NextNumericChar();
        else
            return random.NextAlphabeticChar(uppercase);
    }

    private static char NextAlphanumericChar(this Random random) {
        bool numeric = random.NextBool(AlphanumericProbabilityNumericAny);

        if (numeric)
            return random.NextNumericChar();
        else
            return random.NextAlphabeticChar(random.NextBool());
    }

    private static char NextAlphabeticChar(this Random random, bool uppercase) {
        if (uppercase)
            return (char)random.Next(65, 91);
        else
            return (char)random.Next(97, 123);
    }

    private static char NextAlphabeticChar(this Random random) {
        return random.NextAlphabeticChar(random.NextBool());
    }

    private static char NextNumericChar(this Random random) {
        return (char)random.Next(48, 58);
    }

    public static DateTime NextDateTime(this Random random, DateTime minValue, DateTime maxValue) {
        return DateTime.FromOADate(
            random.NextDouble(minValue.ToOADate(), maxValue.ToOADate())
        );
    }

    public static DateTime NextDateTime(this Random random) {
        return random.NextDateTime(DateTime.MinValue, DateTime.MaxValue);
    }

    public static double NextDouble(this Random random, double minValue, double maxValue) {
        if (maxValue < minValue)
            throw new ArgumentException("Minimum value must be less than maximum value.");

        double difference = maxValue - minValue;
        if (!double.IsInfinity(difference))
            return minValue + (random.NextDouble() * difference);

        else {
            // to avoid evaluating to Double.Infinity, we split the range into two halves:
            double halfDifference = (maxValue * 0.5) - (minValue * 0.5);

            // 50/50 chance of returning a value from the first or second half of the range
            if (random.NextBool())
                return minValue + (random.NextDouble() * halfDifference);
            else
                return (minValue + halfDifference) + (random.NextDouble() * halfDifference);
        }
    }

    public static string NextString(this Random random, int numChars, CharType mode) {
        char[] chars = new char[numChars];

        for (int i = 0; i < numChars; ++i)
            chars[i] = random.NextChar(mode);

        return new string(chars);
    }

    public static string NextString(this Random random, int numChars) {
        return random.NextString(numChars, CharType.AlphanumericAny);
    }

    public static TimeSpan NextTimeSpan(this Random random, TimeSpan minValue, TimeSpan maxValue) {
        return TimeSpan.FromMilliseconds(
            random.NextDouble(minValue.TotalMilliseconds, maxValue.TotalMilliseconds)
        );
    }

    public static TimeSpan NextTimeSpan(this Random random) {
        return random.NextTimeSpan(TimeSpan.MinValue, TimeSpan.MaxValue);
    }
}
public static class DictionaryExtensions
{
    public static Nullable<TValue> GetValueOrNull<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
        where TValue : struct
    {
        TValue result;
        if (dictionary.TryGetValue(key, out result))
            return result;
        else
            return null;
    }
}

Бесплатное использование, просто укажите мое имя (Янко Рёбиш) в коде.

Я обнаруживаю, что делаю это снова и снова, снова ...

public static bool EqualsIgnoreCase(this string a, string b)
{
    return string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
}

... за которыми следуют StartsWithIgnoreCase, EndsWithIgnoreCase и ContainsIgnoreCase.

Поверните это:

DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT @param";

DbParameter param = command.CreateParameter();
param.ParameterName = "@param";
param.Value = "Hello World";

command.Parameters.Add(param);

... в это:

DbCommand command = connection.CreateCommand("SELECT {0}", "Hello World");

... используя этот метод расширения:

using System;
using System.Data.Common;
using System.Globalization;
using System.Reflection;

namespace DbExtensions {

   public static class Db {

      static readonly Func<DbConnection, DbProviderFactory> getDbProviderFactory;
      static readonly Func<DbCommandBuilder, int, string> getParameterName;
      static readonly Func<DbCommandBuilder, int, string> getParameterPlaceholder;

      static Db() {

         getDbProviderFactory = (Func<DbConnection, DbProviderFactory>)Delegate.CreateDelegate(typeof(Func<DbConnection, DbProviderFactory>), typeof(DbConnection).GetProperty("DbProviderFactory", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true));
         getParameterName = (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), typeof(DbCommandBuilder).GetMethod("GetParameterName", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null));
         getParameterPlaceholder = (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), typeof(DbCommandBuilder).GetMethod("GetParameterPlaceholder", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null));
      }

      public static DbProviderFactory GetProviderFactory(this DbConnection connection) {
         return getDbProviderFactory(connection);
      }

      public static DbCommand CreateCommand(this DbConnection connection, string commandText, params object[] parameters) {

         if (connection == null) throw new ArgumentNullException("connection");

         return CreateCommandImpl(GetProviderFactory(connection).CreateCommandBuilder(), connection.CreateCommand(), commandText, parameters);
      }

      private static DbCommand CreateCommandImpl(DbCommandBuilder commandBuilder, DbCommand command, string commandText, params object[] parameters) {

         if (commandBuilder == null) throw new ArgumentNullException("commandBuilder");
         if (command == null) throw new ArgumentNullException("command");
         if (commandText == null) throw new ArgumentNullException("commandText");

         if (parameters == null || parameters.Length == 0) {
            command.CommandText = commandText;
            return command;
         }

         object[] paramPlaceholders = new object[parameters.Length];

         for (int i = 0; i < paramPlaceholders.Length; i++) {

            DbParameter dbParam = command.CreateParameter();
            dbParam.ParameterName = getParameterName(commandBuilder, i);
            dbParam.Value = parameters[i] ?? DBNull.Value;
            command.Parameters.Add(dbParam);

            paramPlaceholders[i] = getParameterPlaceholder(commandBuilder, i);
         }

         command.CommandText = String.Format(CultureInfo.InvariantCulture, commandText, paramPlaceholders);

         return command;
      }
   }
}

Дополнительные методы расширения ADO.NET: DbExtensions

Я только что просмотрел все 4 страницы этого документа и был довольно удивлен, что не нашел способа сократить чек на InvokeRequired:

using System;
using System.Windows.Forms;

/// <summary>
/// Extension methods acting on Control objects.
/// </summary>
internal static class ControlExtensionMethods
{
    /// <summary>
    /// Invokes the given action on the given control's UI thread, if invocation is needed.
    /// </summary>
    /// <param name = "control">Control on whose UI thread to possibly invoke.</param>
    /// <param name = "action">Action to be invoked on the given control.</param>
    public static void MaybeInvoke(this Control control, Action action)
    {
        if (control != null && control.InvokeRequired)
        {
            control.Invoke(action);
        }
        else
        {
            action();
        }
    }

    /// <summary>
    /// Maybe Invoke a Func that returns a value.
    /// </summary>
    /// <typeparam name = "T">Return type of func.</typeparam>
    /// <param name = "control">Control on which to maybe invoke.</param>
    /// <param name = "func">Function returning a value, to invoke.</param>
    /// <returns>The result of the call to func.</returns>
    public static T MaybeInvoke<T>(this Control control, Func<T> func)
    {
        if (control != null && control.InvokeRequired)
        {
            return (T)(control.Invoke(func));
        }
        else
        {
            return func();
        }
    }
}

Использование:

myForm.MaybeInvoke(() => this.Text = "Hello world");

// Sometimes the control might be null, but that's okay.
var dialogResult = this.Parent.MaybeInvoke(() => MessageBox.Show(this, "Yes or no?", "Choice", MessageBoxButtons.YesNo));

Я часто использую это с числами, допускающими значение NULL. Я помогаю поймать это деление на 0, NaN, Infinity ...

public static bool IsNullOrDefault<T>(this T? o) 
    where T : struct
{
        return o == null || o.Value.Equals(default(T));
}

Конечно, если он равен null, вызов завершится неудачно (однажды я пытался реализовать метод IsNullOrEmpty в строке и почувствовал себя глупо, когда понял), но он будет хорошо работать для типов значений

johnc 23.06.2010 07:07

Обнуляемые tpyes имеют встроенное свойство HasValue.

Daniel 23.06.2010 10:04

@johnc, нет, вызов не завершится неудачно, если o равно нулю. Методы расширения - это фактически статические методы, а не методы экземпляра. И метод расширения IsNullOrEmpty у меня отлично работает ...

Thomas Levesque 19.08.2010 23:25

Я считаю весьма полезным следующий метод расширения:

public static T GetService<T>(this IServiceProvider provider)
{
    return (T)provider.GetService(typeof(T));
}

Это значительно упрощает использование интерфейса IServiceProvider. Сравнивать:

IProvideValueTarget target = (IProvideValueTarget)serviceProvider(typeof(IProvideValueTarget));

и

var target = serviceProvider.GetService<IProvideValueTarget>();

Помощник NullPartial HTML для ASP MVC.

При передаче нулевой модели HTML.Partial и HTML.RenderPartial предоставят модель представления, если эта часть строго типизирована и представление имеет другой тип, вместо передачи нулевой ссылки будет выдано исключение. Эти помощники позволяют вам указать два разных частичных объекта, чтобы вы могли убрать свои нулевые тесты из поля зрения.

У вас есть разрешение включить это на страницу Codeplex

public static class nullpartials
    {
        public static MvcHtmlString NullPartial(this HtmlHelper helper, string Partial, string NullPartial, object Model)
        {
            if (Model == null)
                return helper.Partial(NullPartial);
            else
                return helper.Partial(Partial, Model);
        }

        public static MvcHtmlString NullPartial(this HtmlHelper helper, string Partial, string NullPartial, object Model, ViewDataDictionary viewdata)
        {
            if (Model == null)
                return helper.Partial(NullPartial, viewdata);
            else
                return helper.Partial(Partial, Model, viewdata);
        }

        public static void RenderNullPartial(this HtmlHelper helper, string Partial, string NullPartial, object Model)
        {
            if (Model == null)
            {
                helper.RenderPartial(NullPartial);
                return;
            }
            else
            {
                helper.RenderPartial(Partial, Model);
                return;
            }
        }

        public static void RenderNullPartial(this HtmlHelper helper, string Partial, string NullPartial, object Model, ViewDataDictionary viewdata)
        {
            if (Model == null)
            {
                helper.RenderPartial(NullPartial, viewdata);
                return;
            }
            else
            {
                helper.RenderPartial(Partial, Model, viewdata);
                return;
            }
        }
    }

При использовании словаря, в котором ключ является строкой, верните существующий ключ, используя поиск без учета регистра. В нашем случае это были пути к файлам.

/// <summary>
/// Gets the key using <paramref name = "caseInsensitiveKey"/> from <paramref name = "dictionary"/>.
/// </summary>
/// <typeparam name = "T">The dictionary value.</typeparam>
/// <param name = "dictionary">The dictionary.</param>
/// <param name = "caseInsensitiveKey">The case insensitive key.</param>
/// <returns>
/// An existing key; or <see cref = "string.Empty"/> if not found.
/// </returns>
public static string GetKeyIgnoringCase<T>(this IDictionary<string, T> dictionary, string caseInsensitiveKey)
{
    if (string.IsNullOrEmpty(caseInsensitiveKey)) return string.Empty;
    foreach (string key in dictionary.Keys)
    {
        if (key.Equals(caseInsensitiveKey, StringComparison.InvariantCultureIgnoreCase))
        {
            return key;
        }
    }
    return string.Empty;
}

В словаре есть отдельное свойство коллекции ключей, которое может сделать это быстрее.

Joel Coehoorn 03.06.2010 18:40

Если вам нужны ключи без учета регистра, вы можете передать StringComparer.InvariantIgnoreCase конструктору словаря.

Thomas Levesque 19.08.2010 23:49

@ Томас - Даже лучше! Предполагается, что у вас есть доступ к ctor, но это определенно лучший подход.

si618 20.08.2010 04:49

Я использую этот все время:

public static void DelimitedAppend(this StringBuilder sb, string value, string delimiter)
{
    if (sb.Length > 0)
        sb.Append(delimiter);
    sb.Append(value);
}

Это просто гарантирует, что разделитель не будет вставлен, когда строка пуста. Например, чтобы создать список слов, разделенных запятыми:

var farmAnimals = new[] { new { Species = "Dog", IsTasty = false }, new { Species = "Cat", IsTasty = false }, new { Species = "Chicken", IsTasty = true }, };
var soupIngredients = new StringBuilder();
foreach (var edible in farmAnimals.Where(farmAnimal => farmAnimal.IsTasty))
    soupIngredients.DelimitedAppend(edible.Species, ", ");

Намного более быстрый способ получить строку с разделителями - это string.Join(",", strings);, где strings - это массив. Это создает примерно 1/3 IL как метод StringBuilder.

Jim Schubert 23.06.2010 07:53

@ Джим Шуберт: Можете ли вы подтвердить это некоторыми тестами? Размер IL не является хорошим показателем скорости. Меньшее количество инструкций автоматически не означает более быстрое выполнение.

John Leidegren 23.06.2010 10:46

Просто отметьте, что это все еще полезный метод для StringBuilder. Если вы хотите разделить вызовы метода соединения с другим кодом, тогда у вас будет несколько вызовов string.Join в любом случае, что сведет на нет любое преимущество в производительности по сравнению с использованием StringBuilder.

cbp 23.06.2010 10:50

@John: Тесты проводились тысячи раз, и они редко, если вообще когда-либо, показывают какую-либо заметную разницу в производительности. Я хотел бы провести тесты, чтобы увидеть разницу, и сравнить string.Join / Format / Concat, StringBuilder.Append / AppendFormat и конкатенацию операторов +. Однако разницу в 300 мс при построении строки можно легко компенсировать за счет производительности сети и базы данных.

Jim Schubert 23.06.2010 15:32

Подобно строке As и Is выше, но глобально для всех объектов.

Это довольно просто, но я часто использую их, чтобы смягчить взрыв парней с помощью бокса.

public static class ExtensionMethods_Object
{
    [DebuggerStepThrough()]
    public static bool Is<T>(this object item) where T : class
    {
        return item is T;
    }

    [DebuggerStepThrough()]
    public static bool IsNot<T>(this object item) where T : class
    {
        return !(item.Is<T>());
    }

    [DebuggerStepThrough()]
    public static T As<T>(this object item) where T : class
    {
        return item as T;
    }
}

Я рад, что этот код будет использоваться в codeplex, действительно, он уже используется.

И какова цель всего этого? Почему бы просто не написать, скажем, «item as Type» вместо «item.As <Type> ()», как вы это делаете?

Kamarey 23.06.2010 11:30

@Kamarey Это субъективное предпочтение, но оно уменьшает количество запутанных парнеров, которые могут накапливаться, когда у вас есть несколько приведений. item as Type становится (item as Type) или ((Type) item), если вам нужно использовать item как тип приведения. Также сканирование слева направо элемента item.As <Type> (). ... в некоторых запутанных случаях гораздо удобнее читать, чем бокс. Я сказал, что это было просто, и я согласен, что это субъективно, но я считаю, что это может быть довольно мощным инструментом для удобочитаемости кода.

johnc 24.06.2010 02:35

@Kamarey некоторые люди называют это «беглым» программированием - всегда программировать слева направо, никогда не нужно делать резервную копию, чтобы поставить скобки на вещи. Достижение клавиш со стрелками замедляет работу. Он также хорошо сочетается с операторами Enumerable и Observable. @johnc Я бы добавил To<T> в список, который делает (T)item.

scobi 02.10.2010 00:46

Пара полезных расширений, если вы работаете с финансовыми годами

/// <summary>
/// Returns the fiscal year for the passed in date
/// </summary>
/// <param name = "value">the date</param>
/// <returns>the fiscal year</returns>
public static int FiscalYear(this DateTime value)
{
  int ret = value.Year;
  if (value.Month >= 7) ret++;
  return ret;
}

/// <summary>
/// Returns the fiscal year for the passed in date
/// </summary>
/// <param name = "value">the date</param>
/// <returns>the fiscal year</returns>
public static string FiscalYearString(this DateTime value)
{
  int fy = FiscalYear(value);
  return "{0}/{1}".Format(fy - 1, fy);
}

Для элементов управления Winform:

/// <summary>
/// Returns whether the function is being executed during design time in Visual Studio.
/// </summary>
public static bool IsDesignTime(this Control control)
{
    if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
    {
        return true;
    }

    if (control.Site != null && control.Site.DesignMode)
    {
        return true;
    }

    var parent = control.Parent;
    while (parent != null)
    {
        if (parent.Site != null && parent.Site.DesignMode)
        {
            return true;
        }
        parent = parent.Parent;
    }
    return false;
}

/// <summary>
/// Sets the DropDownWidth to ensure that no item's text is cut off.
/// </summary>
public static void SetDropDownWidth(this ComboBox comboBox)
{
    var g = comboBox.CreateGraphics();
    var font = comboBox.Font;
    float maxWidth = 0;

    foreach (var item in comboBox.Items)
    {
        maxWidth = Math.Max(maxWidth, g.MeasureString(item.ToString(), font).Width);
    }

    if (comboBox.Items.Count > comboBox.MaxDropDownItems)
    {
        maxWidth += SystemInformation.VerticalScrollBarWidth;
    }

    comboBox.DropDownWidth = Math.Max(comboBox.Width, Convert.ToInt32(maxWidth));
}

Использование IsDesignTime:

public class SomeForm : Form
{
    public SomeForm()
    {
        InitializeComponent();

        if (this.IsDesignTime())
        {
            return;
        }

        // Do something that makes the visual studio crash or hang if we're in design time,
        // but any other time executes just fine
    }
}

SetDropdownWidth Использование:

ComboBox cbo = new ComboBox { Width = 50 };
cbo.Items.Add("Short");
cbo.Items.Add("A little longer");
cbo.Items.Add("Holy cow, this is a really, really long item. How in the world will it fit?");
cbo.SetDropDownWidth();

Я забыл упомянуть, не стесняйтесь использовать их на Codeplex ...

Как уже упоминалось, это только для WinForms. Он может работать с WPF, но есть проблемы (описанные в комментарии о WPF на msdn.microsoft.com/en-us/library/…). Лучшее решение для WPF, которое я нашел, описано в geekswithblogs.net/lbugnion/archive/2009/09/05/… (хотя, поскольку это статическое свойство, оно на самом деле не работает как метод расширения).

scobi 07.10.2010 20:09
// This file contains extension methods for generic List<> class to operate on sorted lists.
// Duplicate values are OK.
// O(ln(n)) is still much faster then the O(n) of LINQ's searches/filters.
static partial class SortedList
{
    // Return the index of the first element with the key greater then provided.
    // If there's no such element within the provided range, it returns iAfterLast.
    public static int sortedFirstGreaterIndex<tElt, tKey>( this IList<tElt> list, Func<tElt, tKey, int> comparer, tKey key, int iFirst, int iAfterLast )
    {
        if ( iFirst < 0 || iAfterLast < 0 || iFirst > list.Count || iAfterLast > list.Count )
            throw new IndexOutOfRangeException();
        if ( iFirst > iAfterLast )
            throw new ArgumentException();
        if ( iFirst == iAfterLast )
            return iAfterLast;

        int low = iFirst, high = iAfterLast;
        // The code below is inspired by the following article:
        // http://en.wikipedia.org/wiki/Binary_search#Single_comparison_per_iteration
        while( low < high )
        {
            int mid = ( high + low ) / 2;
            // 'mid' might be 'iFirst' in case 'iFirst+1 == iAfterLast'.
            // 'mid' will never be 'iAfterLast'.
            if ( comparer( list[ mid ], key ) <= 0 ) // "< = " since we gonna find the first "greater" element
                low = mid + 1;
            else
                high = mid;
        }
        return low;
    }

    // Return the index of the first element with the key greater then the provided key.
    // If there's no such element, returns list.Count.
    public static int sortedFirstGreaterIndex<tElt, tKey>( this IList<tElt> list, Func<tElt, tKey, int> comparer, tKey key )
    {
        return list.sortedFirstGreaterIndex( comparer, key, 0, list.Count );
    }

    // Add an element to the sorted array.
    // This could be an expensive operation if frequently adding elements that sort firstly.
    // This is cheap operation when adding elements that sort near the tail of the list.
    public static int sortedAdd<tElt>( this List<tElt> list, Func<tElt, tElt, int> comparer, tElt elt )
    {
        if ( list.Count == 0 || comparer( list[ list.Count - 1 ], elt ) <= 0 )
        {
            // either the list is empty, or the item is greater then all elements already in the collection.
            list.Add( elt );
            return list.Count - 1;
        }
        int ind = list.sortedFirstGreaterIndex( comparer, elt );
        list.Insert( ind, elt );
        return ind;
    }

    // Find first exactly equal element, return -1 if not found.
    public static int sortedFindFirstIndex<tElt, tKey>( this List<tElt> list, Func<tElt, tKey, int> comparer, tKey elt )
    {
        int low = 0, high = list.Count - 1;

        while( low < high )
        {
            int mid = ( high + low ) / 2;
            if ( comparer( list[ mid ], elt ) < 0 )
                low = mid + 1;
            else
                high = mid; // this includes the case when we've found an element exactly matching the key
        }
        if ( high >= 0 && 0 == comparer( list[ high ], elt ) )
            return high;
        return -1;
    }

    // Return the IEnumerable that returns array elements in the reverse order.
    public static IEnumerable<tElt> sortedReverse<tElt>( this List<tElt> list )
    {
        for( int i=list.Count - 1; i >= 0; i-- )
            yield return list[ i ];
    }
}

Мое предложение:

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

Работает с коллекциями и массивами:

bool isNullOrEmpty = array.IsNullOrEmpty()

вместо

bool isNullOrEmpty = array == null || array.Length == 0;

Как насчет использования IEnumerable вместо ICollection и Any () вместо Count?

brickner 20.07.2010 09:42

@brickner IEnumerable <T> .Any () - только общий IEnumerable <T> имеет Any ().

Tadas Šukys 20.07.2010 11:04

Преобразует список в таблицу данных

public static class DataTableConverter
{
    /// <summary>
    /// Convert a List{T} to a DataTable.
    /// </summary>
    public static DataTable ToDataTable<T>(this IList<T> items)
    {
        var tb = new DataTable(typeof(T).Name);

        PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo prop in props)
        {
            Type t = GetCoreType(prop.PropertyType);
            tb.Columns.Add(prop.Name, t);
        }

        foreach (T item in items)
        {
            var values = new object[props.Length];

            for (int i = 0; i < props.Length; i++)
            {
                values[i] = props[i].GetValue(item, null);
            }

            tb.Rows.Add(values);
        }

        return tb;
    }

    /// <summary>
    /// Determine of specified type is nullable
    /// </summary>
    public static bool IsNullable(Type t)
    {
        return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
    }

    /// <summary>
    /// Return underlying type if type is Nullable otherwise return the type
    /// </summary>
    public static Type GetCoreType(Type t)
    {
        if (t != null && IsNullable(t))
        {
            if (!t.IsValueType)
            {
                return t;
            }
            else
            {
                return Nullable.GetUnderlyingType(t);
            }
        }
        else
        {
            return t;
        }
    }
}

Использование:

    IList<MyClass> myClassList = new List<MyClass>();
    DataTable myClassDataTable = myClassList.ToDataTable();

И обратное уже включено в .NET в качестве встроенного метода расширения (да, это звучит странно, встроенный метод расширения): stackoverflow.com/questions/208532/…

NickAldwin 23.07.2010 17:22

Я уверен, что это уже делалось раньше, но я часто использую этот метод (и более простые производные):

public static bool CompareEx(this string strA, string strB, CultureInfo culture, bool ignoreCase)
{
    return string.Compare(strA, strB, ignoreCase, culture) == 0;
}

Вы можете написать его разными способами, но мне он нравится, потому что он очень быстро унифицирует мой подход к сравнению строк, сохраняя при этом строки кода (или символы кода).

Это единственное написанное мной расширение, которое я использую регулярно. Это упрощает отправку электронной почты с помощью System.Net.Mail.

public static class MailExtension
{
    // GetEmailCreditial(out strServer) gets credentials from an XML file
    public static void Send(this MailMessage email)
    {
        string strServer = String.Empty;
        NetworkCredential credentials = GetEmailCreditial(out strServer);
        SmtpClient client = new SmtpClient(strServer) { Credentials = credentials };
        client.Send(email);
    }

    public static void Send(this IEnumerable<MailMessage> emails)
    {
        string strServer = String.Empty;
        NetworkCredential credentials = GetEmailCreditial(out strServer);
        SmtpClient client = new SmtpClient(strServer) { Credentials = credentials };
        foreach (MailMessage email in emails)
            client.Send(email);
    }
}

// Example of use: 
new MailMessage("[email protected]","[email protected]","This is an important Subject", "Body goes here").Send();
//Assume email1,email2,email3 are MailMessage objects
new List<MailMessage>(){email1, email2, email}.Send();

Мое наиболее часто используемое расширение - это расширение, которое может форматировать байтовые массивы:

/// <summary>
/// Returns a string representation of a byte array.
/// </summary>
/// <param name = "bytearray">The byte array to represent.</param>
/// <param name = "subdivision">The number of elements per group,
/// or 0 to not restrict it. The default is 0.</param>
/// <param name = "subsubdivision">The number of elements per line,
/// or 0 to not restrict it. The default is 0.</param>
/// <param name = "divider">The string dividing the individual bytes. The default is " ".</param>
/// <param name = "subdivider">The string dividing the groups. The default is "  ".</param>
/// <param name = "subsubdivider">The string dividing the lines. The default is "\r\n".</param>
/// <param name = "uppercase">Whether the representation is in uppercase hexadecimal.
/// The default is <see langword = "true"/>.</param>
/// <param name = "prebyte">The string to put before each byte. The default is an empty string.</param>
/// <param name = "postbyte">The string to put after each byte. The default is an empty string.</param>
/// <returns>The string representation.</returns>
/// <exception cref = "ArgumentNullException">
/// <paramref name = "bytearray"/> is <see langword = "null"/>.
/// </exception>
public static string ToArrayString(this byte[] bytearray,
    int subdivision = 0,
    int subsubdivision = 0,
    string divider = " ",
    string subdivider = "  ",
    string subsubdivider = "\r\n",
    bool uppercase = true,
    string prebyte = "",
    string postbyte = "")
{
    #region Contract
    if (bytearray == null)
        throw new ArgumentNullException("bytearray");
    #endregion

    StringBuilder sb = new StringBuilder(
        bytearray.Length * (2 + divider.Length + prebyte.Length + postbyte.Length) +
        (subdivision > 0 ? (bytearray.Length / subdivision) * subdivider.Length : 0) +
        (subsubdivision > 0 ? (bytearray.Length / subsubdivision) * subsubdivider.Length : 0));
    int groupElements = (subdivision > 0 ? subdivision - 1 : -1);
    int lineElements = (subsubdivision > 0 ? subsubdivision - 1 : -1);
    for (long i = 0; i < bytearray.LongLength - 1; i++)
    {
        sb.Append(prebyte);
        sb.Append(String.Format(CultureInfo.InvariantCulture, (uppercase ? "{0:X2}" : "{0:x2}"), bytearray[i]));
        sb.Append(postbyte);

        if (lineElements == 0)
        {
            sb.Append(subsubdivider);
            groupElements = subdivision;
            lineElements = subsubdivision;
        }
        else if (groupElements == 0)
        {
            sb.Append(subdivider);
            groupElements = subdivision;
        }
        else
            sb.Append(divider);

        lineElements--;
        groupElements--;
    }
    sb.Append(prebyte);
    sb.Append(String.Format(CultureInfo.InvariantCulture, (uppercase ? "{0:X2}" : "{0:x2}"), bytearray[bytearray.LongLength - 1]));
    sb.Append(postbyte);

    return sb.ToString();
}

По умолчанию ToArrayString() просто печатает байтовый массив как длинную строку отдельных байтов. Однако ToArrayString(4, 16) группирует байты в группы по четыре, по 16 байтов в строке, как в вашем любимом шестнадцатеричном редакторе. И следующее красиво форматирует массив байтов для использования в коде C#:

byte[] bytearray = new byte[]{ ... };
Console.Write(bytearray.ToArrayString(4, 16, ", ", ",   ", ",\r\n", true, "0x"));

Он был написан мной, так что вы можете разместить его на Codeplex.

Ух, 8 параметров (не считая параметра this)! ИМХО, любой метод с более чем 4 параметрами требует рефакторинга ...

Thomas Levesque 19.08.2010 23:14

ASP.NET HTML кодирование - коротко и мило:

public static string ToHtmlEncodedString(this string s)
{
    if (String.IsNullOrEmpty(s))
        return s;
    return HttpUtility.HtmlEncode(s);
}

Сравнение строк с подстановочными знаками:

public static bool MatchesWildcard(this string text, string pattern)
{
    int it = 0;
    while (text.CharAt(it) != 0 &&
           pattern.CharAt(it) != '*')
    {
        if (pattern.CharAt(it) != text.CharAt(it) && pattern.CharAt(it) != '?')
            return false;
        it++;
    }

    int cp = 0;
    int mp = 0;
    int ip = it;

    while (text.CharAt(it) != 0)
    {
        if (pattern.CharAt(ip) == '*')
        {
            if (pattern.CharAt(++ip) == 0)
                return true;
            mp = ip;
            cp = it + 1;
        }
        else if (pattern.CharAt(ip) == text.CharAt(it) || pattern.CharAt(ip) == '?')
        {
            ip++;
            it++;
        }
        else
        {
            ip = mp;
            it = cp++;
        }
    }

    while (pattern.CharAt(ip) == '*')
    {
        ip++;
    }
    return pattern.CharAt(ip) == 0;
}

public static char CharAt(this string s, int index)
{
    if (index < s.Length)
        return s[index];
    return '\0';
}

Это прямой перевод кода C из эта статья, следовательно, метод CharAt, который возвращает 0 в конце строки

if (fileName.MatchesWildcard("*.cs"))
{
    Console.WriteLine("{0} is a C# source file", fileName);
}

красивый. То, что я ищу :-)

EightyOne Unite 20.08.2010 12:25

@ aboveyou00, в основном для производительности. Кроме того, нет очевидной эквивалентности между регулярным выражением и шаблоном подстановки, а подстановочный знак проще использовать, если вам не нужна вся мощь регулярного выражения.

Thomas Levesque 21.03.2012 18:43

Эти методы расширения вызывают событие асинхронно. Они были вдохновлены этот ответ StackOverflow.

/// <summary>
/// Invoke an event asynchronously. Each subscriber to the event will be invoked on a separate thread.
/// </summary>
/// <param name = "someEvent">The event to be invoked asynchronously.</param>
/// <param name = "sender">The sender of the event.</param>
/// <param name = "args">The args of the event.</param>
/// <typeparam name = "TEventArgs">The type of <see cref = "EventArgs"/> to be used with the event.</typeparam>
public static void InvokeAsync<TEventArgs>(this EventHandler<TEventArgs> someEvent, object sender, TEventArgs args)
    where TEventArgs : EventArgs
{
    if (someEvent == null)
    {
        return;
    }

    var eventListeners = someEvent.GetInvocationList();

    AsyncCallback endAsyncCallback = delegate(IAsyncResult iar)
    {
        var ar = iar as AsyncResult;
        if (ar == null)
        {
            return;
        }

        var invokedMethod = ar.AsyncDelegate as EventHandler<TEventArgs>;
        if (invokedMethod != null)
        {
            invokedMethod.EndInvoke(iar);
        }
    };

    foreach (EventHandler<TEventArgs> methodToInvoke in eventListeners)
    {
        methodToInvoke.BeginInvoke(sender, args, endAsyncCallback, null);
    }
}

/// <summary>
/// Invoke an event asynchronously. Each subscriber to the event will be invoked on a separate thread.
/// </summary>
/// <param name = "someEvent">The event to be invoked asynchronously.</param>
/// <param name = "sender">The sender of the event.</param>
/// <param name = "args">The args of the event.</param>
public static void InvokeAsync(this EventHandler someEvent, object sender, EventArgs args)
{
    if (someEvent == null)
    {
        return;
    }

    var eventListeners = someEvent.GetInvocationList();

    AsyncCallback endAsyncCallback = delegate(IAsyncResult iar)
    {
        var ar = iar as AsyncResult;
        if (ar == null)
        {
            return;
        }

        var invokedMethod = ar.AsyncDelegate as EventHandler;
        if (invokedMethod != null)
        {
            invokedMethod.EndInvoke(iar);
        }
    };

    foreach (EventHandler methodToInvoke in eventListeners)
    {
        methodToInvoke.BeginInvoke(sender, args, endAsyncCallback, null);
    }
}

Использовать:

public class Foo
{
    public event EventHandler<EventArgs> Bar;

    public void OnBar()
    {
        Bar.InvokeAsync(this, EventArgs.Empty);
    }
}

Обратите внимание на дополнительное преимущество, заключающееся в том, что вам не нужно проверять значение null для события перед его вызовом. например.:

EventHandler<EventArgs> handler = Bar;
if (handler != null)
{
    // Invoke the event
}

Тестировать:

void Main()
{
    EventHandler<EventArgs> handler1 =
    delegate(object sender, EventArgs args)
    {
        // Simulate performing work in handler1
        Thread.Sleep(100);
        Console.WriteLine("Handled 1");
    };

    EventHandler<EventArgs> handler2 =
    delegate(object sender, EventArgs args)
    {
        // Simulate performing work in handler2
        Thread.Sleep(50);
        Console.WriteLine("Handled 2");
    };

    EventHandler<EventArgs> handler3 =
    delegate(object sender, EventArgs args)
    {
        // Simulate performing work in handler3
        Thread.Sleep(25);
        Console.WriteLine("Handled 3");
    };

    var foo = new Foo();
    foo.Bar += handler1;
    foo.Bar += handler2;
    foo.Bar += handler3;
    foo.OnBar();

    Console.WriteLine("Start executing important stuff");

    // Simulate performing some important stuff here, where we don't want to
    // wait around for the event handlers to finish executing
    Thread.Sleep(1000);

    Console.WriteLine("Finished executing important stuff");
}

Вызов события (обычно) дает следующий результат:

Start executing important stuff
Handled 3
Handled 2
Handled 1
Finished executing important stuff

Если бы событие было вызвано синхронно, всегда выдало бы этот вывод - и задержало бы выполнение "важных" вещей:

Handled 1
Handled 2
Handled 3
Start executing important stuff
Finished executing important stuff

Было бы здорово иметь дату и время в формате Unix TimeStamp и ISO 8601. активно используется на веб-сайтах и ​​в сервисах отдыха.

Я использую его в своей библиотеке Facebook. Вы можете найти исходник http://github.com/prabirshrestha/FacebookSharp/blob/master/src/FacebookSharp.Core/FacebookUtils/DateUtils.cs

private static readonly DateTime EPOCH = DateTime.SpecifyKind(new DateTime(1970, 1, 1, 0, 0, 0, 0),DateTimeKind.Utc);

public static DateTime FromUnixTimestamp(long timestamp)
{
    return EPOCH.AddSeconds(timestamp);
}

public static long ToUnixTimestamp(DateTime date)
{
    TimeSpan diff = date.ToUniversalTime() - EPOCH;
    return (long)diff.TotalSeconds;
}

public static DateTime FromIso8601FormattedDateTime(string iso8601DateTime){
    return DateTime.ParseExact(iso8601DateTime, "o", System.Globalization.CultureInfo.InvariantCulture);
}

public static string ToIso8601FormattedDateTime(DateTime dateTime)
{
    return dateTime.ToString("o");
}

Не стесняйтесь использовать в проекте codeplex.

Я сам не раз писал практически идентичный код.

Greg D 11.09.2010 17:42

Интересно. Но не совсем методы расширения.

fre0n 14.09.2010 00:13

Перезаписать часть строки по указанному индексу.

Мне приходится работать с системой, которая ожидает, что некоторые входные значения будут фиксированной шириной и строками с фиксированным положением.

public static string Overwrite(this string s, int startIndex, string newStringValue)
{
  return s.Remove(startIndex, newStringValue.Length).Insert(startIndex, newStringValue);
}

Итак, я могу:

string s = new String(' ',60);
s = s.Overwrite(7,"NewValue");

Это хороший метод расширения, учитывая, что String.Replace не изменяет исходную строку.

Alex Essilfie 06.05.2011 20:57

Некоторые функции даты:

public static bool IsFuture(this DateTime date, DateTime from)
{
    return date.Date > from.Date;
}

public static bool IsFuture(this DateTime date)
{
    return date.IsFuture(DateTime.Now);
}

public static bool IsPast(this DateTime date, DateTime from)
{
    return date.Date < from.Date;
}

public static bool IsPast(this DateTime date)
{
    return date.IsPast(DateTime.Now);
}

В нашей кодовой базе есть несколько похожих: IsBefore (), IsOnOrBefore (), IsOnOrAfter (), IsAfter (), IsBeforeToday (), IsAfterToday (). В них заключен довольно тривиальный код, но они значительно улучшают читаемость.

KeithS 23.10.2010 02:25

Одно из моих любимых - расширение IsLike () для String. IsLike () соответствует Оператор Like в VB и удобен, когда вы не хотите писать полноценное регулярное выражение для решения вашей проблемы. Использование будет примерно таким:

"abc".IsLike("a*"); // true
"Abc".IsLike("[A-Z][a-z][a-z]"); // true
"abc123".IsLike("*###"); // true
"hat".IsLike("?at"); // true
"joe".IsLike("[!aeiou]*"); // true

"joe".IsLike("?at"); // false
"joe".IsLike("[A-Z][a-z][a-z]"); // false

Вот код

public static class StringEntentions {
    /// <summary>
    /// Indicates whether the current string matches the supplied wildcard pattern.  Behaves the same
    /// as VB's "Like" Operator.
    /// </summary>
    /// <param name = "s">The string instance where the extension method is called</param>
    /// <param name = "wildcardPattern">The wildcard pattern to match.  Syntax matches VB's Like operator.</param>
    /// <returns>true if the string matches the supplied pattern, false otherwise.</returns>
    /// <remarks>See http://msdn.microsoft.com/en-us/library/swf8kaxw(v=VS.100).aspx</remarks>
    public static bool IsLike(this string s, string wildcardPattern) {
        if (s == null || String.IsNullOrEmpty(wildcardPattern)) return false;
        // turn into regex pattern, and match the whole string with ^$
        var regexPattern = "^" + Regex.Escape(wildcardPattern) + "$";

        // add support for ?, #, *, [], and [!]
        regexPattern = regexPattern.Replace(@"\[!", "[^")
                                   .Replace(@"\[", "[")
                                   .Replace(@"\]", "]")
                                   .Replace(@"\?", ".")
                                   .Replace(@"\*", ".*")
                                   .Replace(@"\#", @"\d");

        var result = false;
        try {
            result = Regex.IsMatch(s, regexPattern);
        }
        catch (ArgumentException ex) {
            throw new ArgumentException(String.Format("Invalid pattern: {0}", wildcardPattern), ex);
        }
        return result;
    }
}

По назначению очень похоже на метод, который я опубликовал здесь. Ваша реализация позволяет использовать более гибкие шаблоны, но моя, вероятно, быстрее;)

Thomas Levesque 01.10.2010 01:20

Вот тот, который я только что создал.

// requires .NET 4

public static TReturn NullOr<TIn, TReturn>(this TIn obj, Func<TIn, TReturn> func,
        TReturn elseValue = default(TReturn)) where TIn : class
    { return obj != null ? func(obj) : elseValue; }

// versions for CLR 2, which doesn't support optional params

public static TReturn NullOr<TIn, TReturn>(this TIn obj, Func<TIn, TReturn> func,
        TReturn elseValue) where TIn : class
    { return obj != null ? func(obj) : elseValue; }
public static TReturn NullOr<TIn, TReturn>(this TIn obj, Func<TIn, TReturn> func)
        where TIn : class
    { return obj != null ? func(obj) : default(TReturn); }

Это позволяет вам делать это:

var lname = thingy.NullOr(t => t.Name).NullOr(n => n.ToLower());

что более свободно и (IMO) легче читать, чем это:

var lname = (thingy != null ? thingy.Name : null) != null
    ? thingy.Name.ToLower() : null;

Что, если мне нужен thingy.NullOr(t => t.Count), где Count - это целое число? Вы должны вернуть default(TReturn), а не null, в этом случае ограничение class вам не понадобится, и оно будет работать и для типов значений.

Thomas Levesque 02.10.2010 00:22

TIn должен быть классом, иначе весь этот метод расширения не имеет смысла (типы значений не могут быть нулевыми). И ваш пример с t.Count действительно работает с указанным выше методом расширения. Не могли бы вы взглянуть еще раз?

scobi 02.10.2010 00:41

@Scott: это полезный метод решения распространенной проблемы. Однако я считаю, что TReturn elseValue = default(TReturn) доступен только для .NET 4.0? У меня 3.5 SP1, и я никогда не видел эту конструкцию (как и мой компилятор). Я просто переместил это внутрь метода. Однако одна проблема заключается в том, что упаковка типа, допускающего значение NULL, в объект для использования с методом дает неожиданный результат (0 против ожидаемого NULL).

Jim Schubert 02.10.2010 04:51

@Jim: ключевое слово default(T) существует с VS2005, но я думаю, что параметры по умолчанию - это новая функция .NET 4. Самый простой способ обойти это - иметь два варианта: один, который принимает параметр, а другой - нет. Я обновлю ответ, чтобы он был совместим с CLR 2.0. По поводу бокса - в этом суть default. Это будут 0-инициализированные данные для типа значения и null для всех ссылочных типов. TReturn типа значения должен оставаться распакованным на протяжении всей функции.

scobi 04.10.2010 06:04

@Scott: Мой вопрос касался параметра по умолчанию, который я видел только в динамических языках, таких как Ruby. Моя точка зрения относительно типов, допускающих значение NULL, заключается в том, что возвращаемый x.Value должен возвращать значение NULL (если, например, int? был нулевым) или значение, если int? имеет значение. Возврат 0, когда int? x = null передается и помещается в объект, является странным случаем. Я видел аналогичные проверки типов, допускающих значение NULL, в библиотеках, таких как fluent nhibernate и linfu (я думаю) для этого конкретного случая, что позволяет вам отказаться от ограничения класса, как предлагалось ранее.

Jim Schubert 05.10.2010 06:17

@Jim Я считаю, что такой параметр по умолчанию также возможен в C++. Я не знаю, почему это было бы распространено только на динамическом языке, потому что это статически типизированная функция (параметры шаблона известны во время компиляции). Что касается int?, требуется ограничение класса, иначе функция не будет компилироваться. Вы не можете сравнивать с null, не требуя ссылочного типа. Теперь можно иметь вторую версию функции, которая принимает TIn?. Но что она должна делать? Вернуть нуль или Value в штучной упаковке? Но у нас это уже есть в форме обнуляемого ... Я не вижу добавленной стоимости ...

scobi 05.10.2010 19:34

@Scott: Спасибо, что упомянули параметры по умолчанию в C++. Я видел их на C++, но это было в школе, и я давно о них забыл. В Ruby они называются «необязательными параметрами», а не «параметрами по умолчанию», и я связал их с динамическими языками, потому что они определяют тип параметра. В случае Ruby тип переменной нельзя изменить, но переменную можно переназначить. Я слышал о параметрах по умолчанию в VB.NET под 3.5, и мне достаточно перейти на 4.0. Однако вы правы в том, что добавление значений NULL мало.

Jim Schubert 06.10.2010 01:47

... Я просто подумал, что стоит упомянуть о побочных эффектах упаковки структуры.

Jim Schubert 06.10.2010 01:49

В духе String.IsNullOrEmpty

Чтобы проверить, что данный список является нулевым или пустым

public static bool IsNullOrEmpty<TSource>(this List<TSource> src)
{            
    return (src == null || src.Count == 0);
}

И это для проверки данных 2 файлов и свойств

public static bool Compare(this FileInfo f1, FileInfo f2, string propertyName)
{
    try
    {
        PropertyInfo p1 = f1.GetType().GetProperty(propertyName);
        PropertyInfo p2 = f2.GetType().GetProperty(propertyName);

        if (p1.GetValue(f1, null) == p2.GetValue(f1, null))
            return true;
    }
    catch (Exception ex)
    {
        return false;
    }
    return false;
}

И используйте это так

FileInfo fo = new FileInfo("c:\\netlog.txt");
FileInfo f1 = new FileInfo("c:\\regkey.txt");

fo.compare(f1, "CreationTime");

Может быть extende, чтобы быть более общим, тоже public static bool CompareByProperty<Tself, Tother>(this Tself self, Tother other, string propertyName), но вам придется изменить сравнение, чтобы использовать Equals(value1, value2) вместо ==, поскольку == сравнивает ссылку для типов объектов: stackoverflow.com/questions/814878/…

Jürgen Steinblock 14.10.2010 16:02

((src == null || src.Count == 0)? true: false) == (src == null || src.Count == 0)

Simen Echholt 14.10.2010 16:46

Почему не IsNullOrEmpty<T>(), который принимает IEnumerable<T>?

jpbochi 14.10.2010 17:20

Кстати, вы потеряли мой голос, добавив этот ? true : false.

jpbochi 14.10.2010 17:22

Это следует разделить на два отдельных ответа. Первый в порядке (должен быть IEnumerable <T> вместо List <T>). Второй сомнительный. Я согласен с @SchlaWiener, сделайте это общим. Я бы тоже потерял catch. Если возникает исключение, позвольте вызывающей стороне решить, как его обработать.

Greg 14.10.2010 19:23

Метод IsNullOrEmpty - хорошая идея, я использую его все время (но с IEnumerable <T>, а не List <T>) ... но я почти уверен, что он уже был опубликован. Что касается второго метода, я не вижу причин ограничивать его только FileInfo. Кроме того, передача строки с именем свойства - плохая идея: вы можете сделать то же самое с делегатом, и вы избежите накладных расходов на отражение

Thomas Levesque 15.10.2010 00:18

Вот забавный пример из нашей кодовой базы в действии. Выполните дорогостоящее перечисление с отложенным вычислением в потоке задания и отправьте результаты обратно через наблюдаемый объект.

public static IObservable<T> ToAsyncObservable<T>(this IEnumerable<T> @this)
{
    return Observable.Create<T>(observer =>
    {
        var task = new Task(() =>
        {
            try
            {
                @this.Run(observer.OnNext);
                observer.OnCompleted();
            }
            catch (Exception e)
            {
                observer.OnError(e);
            }
        });

        task.Start();

        return () => { };
    });
}

Глупый образец:

new DirectoryInfo(@"c:\program files")
    .EnumerateFiles("*", SearchOption.AllDirectories)
    .ToAsyncObservable()
    .BufferWithTime(TimeSpan.FromSeconds(0.5))
    .ObserveOnDispatcher()
    .Subscribe(
        l => Console.WriteLine("{0} received", l.Count),
        () => Console.WriteLine("Done!"));

for (;;)
{
    Thread.Sleep(10);
    Dispatcher.PushFrame(new DispatcherFrame());
}

Очевидно, что это расширение будет бесполезно для вас, если вы не используете замечательные Reactive Extensions!

ОБНОВИТЬ спасибо Ричарду в комментариях, этот метод расширения не нужен. RX уже имеет метод расширения ToObservable, который принимает IScheduler. Используйте это вместо этого!

Не уверен, что я думаю об использовании @this в качестве имени параметра. С одной стороны, поскольку это метод расширения, вы можете рассматривать его как обычный метод класса. С другой стороны, использование (общего) ключевого слова в качестве имени параметра меня неправильно расстраивает. Хотя звучит интересно.

Davy8 14.10.2010 20:59

Изначально я подобрал @this где-нибудь в блоге или, может быть, здесь, на SO. Изначально у меня были те же опасения, но я использовал его для всех своих методов расширения в течение последних нескольких недель, и мне он очень понравился. Я думаю, что «можно думать об этом как о методе обычного класса» довольно сильно перевешивает озабоченность по поводу повторного использования ключевого слова. Я действительно думал об использовании «th» или «self» для имен, но мне особенно нравится, как действительно выскакивает @. Он постоянно напоминает мне, какой у меня метод.

scobi 14.10.2010 21:06

Привет, Скотт, у меня действительно нет большого опыта работы с задачами или Rx, и мне трудно следить за реализацией этого метода. Полезно ли это, когда оценка отдельных элементов в последовательности является дорогостоящей (что требует async eval)? Создает ли он новый поток для каждого элемента или повторно использует тот же поток заданий по мере добавления большего количества элементов?

xofz 14.10.2010 21:16

Это полезно, когда последовательность дорогая. Один поток извлекается из пула для асинхронного обхода перечислимого объекта. Он не возвращается до тех пор, пока перечислимое не будет завершено или не будет создано исключение. Технически, в этом примере нет необходимости в диспетчере. Я включил его, потому что я пишу много кода WPF, и это частый мой шаблон: отправить задачу, сделать что-то, опубликовать ее как наблюдаемую, отправить результаты через очередь сообщений потока пользовательского интерфейса.

scobi 14.10.2010 21:42

Есть ли причина, по которой enumerable.ToObservable(Scheduler.TaskPool) не решает ту же проблему?

Richard Szalay 14.10.2010 23:08

@ Ричард - нет! Не знал об этой функции. :)

scobi 14.10.2010 23:43

Я на самом деле просто блоггинг это сегодня. Это строго типизированная реактивная оболочка для свойства INotifyPropertyChanged.

GetPropertyValues возвращает IObservable<T> значений по мере их изменения, начиная с текущего значения. Если игнорировать текущее значение, вы можете просто вызвать Skip(1) для получения результата.

Использование выглядит так:

IObservable<int> values = viewModel.GetPropertyValues(x => x.IntProperty);

Выполнение:

public static class NotifyPropertyChangeReactiveExtensions
{
    // Returns the values of property (an Expression) as they change, 
    // starting with the current value
    public static IObservable<TValue> GetPropertyValues<TSource, TValue>(
        this TSource source, Expression<Func<TSource, TValue>> property)
        where TSource : INotifyPropertyChanged
    {
        MemberExpression memberExpression = property.Body as MemberExpression;

        if (memberExpression == null)
        {
            throw new ArgumentException(
                "property must directly access a property of the source");
        }

        string propertyName = memberExpression.Member.Name;

        Func<TSource, TValue> accessor = property.Compile();

        return source.GetPropertyChangedEvents()
            .Where(x => x.EventArgs.PropertyName == propertyName)
            .Select(x => accessor(source))
            .StartWith(accessor(source));
    }

    // This is a wrapper around FromEvent(PropertyChanged)
    public static IObservable<IEvent<PropertyChangedEventArgs>>
        GetPropertyChangedEvents(this INotifyPropertyChanged source)
    {
        return Observable.FromEvent<PropertyChangedEventHandler, 
            PropertyChangedEventArgs>(
            h => new PropertyChangedEventHandler(h),
            h => source.PropertyChanged += h,
            h => source.PropertyChanged -= h);
    }
}

Для краткого описания событий:

public static void Raise(this EventHandler handler, object sender, EventArgs e)
{
    if (handler != null)
    {
        handler(sender, e);
    }
}

public static void Raise<T>(this EventHandler<T> handler, object sender, T e) where T : EventArgs
{
    if (handler != null)
    {
        handler(sender, e);
    }
}

Использование:

public event EventHandler Bar;

public void Foo()
{
    Bar.Raise(this, EventArgs.Empty);
}

Немного обсуждается потенциальная безопасность потоков здесь. Начиная с .NET 4, указанная выше форма является поточно-ориентированной, но требует перегруппировки и некоторых блокировок при использовании более старой версии.

У этого решения есть проблема: вы можете использовать его только в том случае, если событие объявлено в текущем классе, а не если оно объявлено в базовом классе ...

Thomas Levesque 15.10.2010 13:34

@Thomas: Разве это не верно для создания событий вручную? Решение состоит в том, чтобы использовать шаблон protected virtual void OnBar(Eventargs e), описанный здесь: msdn.microsoft.com/en-us/library/hy3sefw3%28VS.80%29.aspx.

Will Vousden 15.10.2010 16:09

Да, но я имею в виду, что ваше решение не работает в общем случае

Thomas Levesque 15.10.2010 16:38

Собственно, в старых версиях это тоже потокобезопасно. Я сам написал идентичные методы.

SLaks 15.10.2010 23:23

+1 - я вообще ненавижу методы расширения для Null-объектов; однако это действительно хорошее использование!

csharptest.net 25.10.2010 21:58

Я написал как четыре миллиона методов расширения, поэтому вот несколько из них, которые я считаю особенно полезными. Не стесняйтесь реализовывать.

public static class ControlExtenders
{
    /// <summary>
    /// Advanced version of find control.
    /// </summary>
    /// <typeparam name = "T">Type of control to find.</typeparam>
    /// <param name = "id">Control id to find.</param>
    /// <returns>Control of given type.</returns>
    /// <remarks>
    /// If the control with the given id is not found
    /// a new control instance of the given type is returned.
    /// </remarks>
    public static T FindControl<T>(this Control control, string id) where T : Control
    {
        // User normal FindControl method to get the control
        Control _control = control.FindControl(id);

        // If control was found and is of the correct type we return it
        if (_control != null && _control is T)
        {
            // Return new control
            return (T)_control;
        }

        // Create new control instance
        _control = (T)Activator.CreateInstance(typeof(T));

        // Add control to source control so the
        // next it is found and the value can be
        // passed on itd, remember to hide it and
        // set an ID so it can be found next time
        if (!(_control is ExtenderControlBase))
        {
            _control.Visible = false;
        }
        _control.ID = id;
        control.Controls.Add(_control);

        // Use reflection to create a new instance of the control
        return (T)_control;
    }
}

public static class GenericListExtenders
{
    /// <summary>
    /// Sorts a generic list by items properties.
    /// </summary>
    /// <typeparam name = "T">Type of collection.</typeparam>
    /// <param name = "list">Generic list.</param>
    /// <param name = "fieldName">Field to sort data on.</param>
    /// <param name = "sortDirection">Sort direction.</param>
    /// <remarks>
    /// Use this method when a dinamyc sort field is requiered. If the 
    /// sorting field is known manual sorting might improve performance.
    /// </remarks>
    public static void SortObjects<T>(this List<T> list, string fieldName, SortDirection sortDirection)
    {
        PropertyInfo propInfo = typeof(T).GetProperty(fieldName);
        if (propInfo != null)
        {
            Comparison<T> compare = delegate(T a, T b)
            {
                bool asc = sortDirection == SortDirection.Ascending;
                object valueA = asc ? propInfo.GetValue(a, null) : propInfo.GetValue(b, null);
                object valueB = asc ? propInfo.GetValue(b, null) : propInfo.GetValue(a, null);
                return valueA is IComparable ? ((IComparable)valueA).CompareTo(valueB) : 0;
            };
            list.Sort(compare);
        }
    }

    /// <summary>
    /// Creates a pagged collection from generic list.
    /// </summary>
    /// <typeparam name = "T">Type of collection.</typeparam>
    /// <param name = "list">Generic list.</param>
    /// <param name = "sortField">Field to sort data on.</param>
    /// <param name = "sortDirection">Sort direction.</param>
    /// <param name = "from">Page from item index.</param>
    /// <param name = "to">Page to item index.</param>
    /// <param name = "copy">Creates a copy and returns a new list instead of changing the current one.</param>
    /// <returns>Pagged list collection.</returns>
    public static List<T> Page<T>(this List<T> list, string sortField, bool sortDirection, int from, int to, bool copy)
    {
        List<T> _pageList = new List<T>();

        // Copy list
        if (copy)
        {
            T[] _arrList = new T[list.Count];
            list.CopyTo(_arrList);
            _pageList = new List<T>(_arrList);
        }
        else
        {
            _pageList = list;
        }

        // Make sure there are enough items in the list
        if (from > _pageList.Count)
        {
            int diff = Math.Abs(from - to);
            from = _pageList.Count - diff;
        }
        if (to > _pageList.Count)
        {
            to = _pageList.Count;
        }

        // Sort items
        if (!string.IsNullOrEmpty(sortField))
        {
            SortDirection sortDir = SortDirection.Descending;
            if (!sortDirection) sortDir = SortDirection.Ascending;
            _pageList.SortObjects(sortField, sortDir);
        }

        // Calculate max number of items per page
        int count = to - from;
        if (from + count > _pageList.Count) count -= (from + count) - _pageList.Count;

        // Get max number of items per page
        T[] pagged = new T[count];
        _pageList.CopyTo(from, pagged, 0, count);

        // Return pagged items
        return new List<T>(pagged);
    }

    /// <summary>
    /// Shuffle's list items.
    /// </summary>
    /// <typeparam name = "T">List type.</typeparam>
    /// <param name = "list">Generic list.</param>
    public static void Shuffle<T>(this List<T> list)
    {
        Random rng = new Random();
        for (int i = list.Count - 1; i > 0; i--)
        {
            int swapIndex = rng.Next(i + 1);
            if (swapIndex != i)
            {
                T tmp = list[swapIndex];
                list[swapIndex] = list[i];
                list[i] = tmp;
            }
        }
    }

    /// <summary>
    /// Converts generic List to DataTable.
    /// </summary>
    /// <typeparam name = "T">Type.</typeparam>
    /// <param name = "list">Generic list.</param>
    /// <param name = "columns">Name of the columns to copy to the DataTable.</param>
    /// <returns>DataTable.</returns>
    public static DataTable ToDataTable<T>(this List<T> list, string[] columns)
    {
        List<string> _columns = new List<string>(columns);
        DataTable dt = new DataTable();

        foreach (PropertyInfo info in typeof(T).GetProperties())
        {
            if (_columns.Contains(info.Name) || columns == null)
            {
                dt.Columns.Add(new DataColumn(info.Name, info.PropertyType));
            }
        }
        foreach (T t in list)
        {
            DataRow row = dt.NewRow();
            foreach (PropertyInfo info in typeof(T).GetProperties())
            {
                if (_columns.Contains(info.Name) || columns == null)
                {
                    row[info.Name] = info.GetValue(t, null);
                }
            }
            dt.Rows.Add(row);
        }
        return dt;
    }
}

public static class DateTimeExtenders
{
    /// <summary>
    /// Returns number of month from a string representation.
    /// </summary>
    /// <returns>Number of month.</returns>
    public static int MonthToNumber(this DateTime datetime, string month)
    {
        month = month.ToLower();
        for (int i = 1; i <= 12; i++)
        {
            DateTime _dt = DateTime.Parse("1." + i + ".2000");
            string _month = CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(i).ToLower();
            if (_month == month)
            {
                return i;
            }
        }
        return 0;
    }

    /// <summary>
    /// Returns month name from month number.
    /// </summary>
    /// <returns>Name of month.</returns>
    public static string MonthToName(this DateTime datetime, int month)
    {
        for (int i = 1; i <= 12; i++)
        {
            if (i == month)
            {
                return CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(i);
            }
        }
        return "";
    }
}

public static class ObjectExtender
{
    public static object CloneBinary<T>(this T originalObject)
    {
        using (var stream = new System.IO.MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(stream, originalObject);
            stream.Position = 0;
            return (T)binaryFormatter.Deserialize(stream);
        }
    }

    public static object CloneObject(this object obj)
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
            binaryFormatter.Serialize(memStream, obj);
            memStream.Position = 0;
            return binaryFormatter.Deserialize(memStream);
        }
    }
}

public static class StringExtenders
{
    /// <summary>
    /// Returns string as unit.
    /// </summary>
    /// <param name = "value">Value.</param>
    /// <returns>Unit</returns>
    public static Unit ToUnit(this string value)
    {
        // Return empty unit
        if (string.IsNullOrEmpty(value))
            return Unit.Empty;

        // Trim value
        value = value.Trim();

        // Return pixel unit
        if (value.EndsWith("px"))
        {
            // Set unit type
            string _int = value.Replace("px", "");

            // Try parsing to int
            double _val = 0;
            if (!double.TryParse(_int, out _val))
            {
                // Invalid value
                return Unit.Empty;
            }

            // Return unit
            return new Unit(_val, UnitType.Pixel);
        }

        // Return percent unit
        if (value.EndsWith("%"))
        {
            // Set unit type
            string _int = value.Replace("%", "");

            // Try parsing to int
            double _val = 0;
            if (!double.TryParse(_int, out _val))
            {
                // Invalid value
                return Unit.Empty;
            }

            // Return unit
            return new Unit(_val, UnitType.Percentage);
        }

        // No match found
        return new Unit();
    }

    /// <summary>
    /// Returns alternative string if current string is null or empty.
    /// </summary>
    /// <param name = "str"></param>
    /// <param name = "alternative"></param>
    /// <returns></returns>
    public static string Alternative(this string str, string alternative)
    {
        if (string.IsNullOrEmpty(str)) return alternative;
        return str;
    }

    /// <summary>
    /// Removes all HTML tags from string.
    /// </summary>
    /// <param name = "html">String containing HTML tags.</param>
    /// <returns>String with no HTML tags.</returns>
    public static string StripHTML(this string html)
    {
        string nohtml = Regex.Replace(html, "<(.|\n)*?>", "");
        nohtml = nohtml.Replace("\r\n", "").Replace("\n", "").Replace("&nbsp;", "").Trim();
        return nohtml;
    }
}

Первый - мой любимый, так как он позволяет мне заменить:

Control c = this.FindControl("tbName");
if (c != null)
{
    // Do something with c
    customer.Name = ((TextBox)c).Text;
}

С этим:

TextBox c = this.FindControl<TextBox>("tbName");
customer.Name = c.Text;

Строковые значения по умолчанию:

string str = "";
if (string.IsNullOrEmpty(str))
{
    str = "I'm empty!";
}

Становится:

str = str.Alternative("I'm empty!");

Не проверял весь поток, возможно, он уже здесь, но:

public static class FluentOrderingExtensions
    public class FluentOrderer<T> : IEnumerable<T>
    {
        internal List<Comparison<T>> Comparers = new List<Comparison<T>>();

        internal IEnumerable<T> Source;

        public FluentOrderer(IEnumerable<T> source)
        {
            Source = source;
        }

        #region Implementation of IEnumerable

        public IEnumerator<T> GetEnumerator()
        {
            var workingArray = Source.ToArray();
            Array.Sort(workingArray, IterativeComparison);

            foreach(var element in workingArray) yield return element;
        }

        private int IterativeComparison(T a, T b)
        {
            foreach (var comparer in Comparers)
            {
                var result = comparer(a,b);
                if (result != 0) return result;
            }
            return 0;
        }

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

        #endregion
    }

    public static FluentOrderer<T> OrderFluentlyBy<T,TResult>(this IEnumerable<T> source, Func<T,TResult> predicate) 
        where TResult : IComparable<TResult>
    {
        var result = new FluentOrderer<T>(source);
        result.Comparers.Add((a,b)=>predicate(a).CompareTo(predicate(b)));
        return result;
    }

    public static FluentOrderer<T> OrderFluentlyByDescending<T,TResult>(this IEnumerable<T> source, Func<T,TResult> predicate) 
        where TResult : IComparable<TResult>
    {
        var result = new FluentOrderer<T>(source);
        result.Comparers.Add((a,b)=>predicate(a).CompareTo(predicate(b)) * -1);
        return result;
    }

    public static FluentOrderer<T> ThenBy<T, TResult>(this FluentOrderer<T> source, Func<T, TResult> predicate)
        where TResult : IComparable<TResult>
    {
        source.Comparers.Add((a, b) => predicate(a).CompareTo(predicate(b)));
        return source;
    }

    public static FluentOrderer<T> ThenByDescending<T, TResult>(this FluentOrderer<T> source, Func<T, TResult> predicate)
        where TResult : IComparable<TResult>
    {
        source.Comparers.Add((a, b) => predicate(a).CompareTo(predicate(b)) * -1);
        return source;
    }
}

Использование:

var myFluentlyOrderedList = GetABunchOfComplexObjects()
    .OrderFluentlyBy(x=>x.PropertyA)
    .ThenByDescending(x=>x.PropertyB)
    .ThenBy(x=>x.SomeMethod())
    .ThenBy(x=>SomeOtherMethodAppliedTo(x))
    .ToList();

... при условии, конечно, что все предикаты возвращают типы, которые IComparable сами с собой. Было бы лучше работать со стабильной сортировкой, такой как MergeSort, вместо встроенной в .NET QuickSort, но она предоставляет вам удобочитаемую возможность упорядочивания по многим полям, аналогичную SQL (в любом случае, насколько это возможно для цепочки методов). Вы можете расширить это, чтобы разместить элементы, которые не являются IComparable, путем определения перегрузок, которые принимают лямбду сравнения вместо того, чтобы создавать ее на основе предиката.

Обновлено: небольшое объяснение, поскольку комментатор получил некоторые всплески: этот набор методов улучшает базовую функциональность OrderBy (), позволяя вам сортировать на основе нескольких полей в порядке убывания важности. Пример из реальной жизни - это сортировка списка счетов по клиенту, а затем по номеру счета (или дате счета). Другие методы получения данных в этом порядке либо не сработают (OrderBy () использует нестабильную сортировку, поэтому его нельзя связать в цепочку), либо будут неэффективными и не будут выглядеть так, как будто он выполняет то, что вы пытаетесь сделать.

В чем это преимущество перед OrderBy и OrderByDescending по умолчанию?

Agent_9191 20.10.2010 15:38

OrderBy () нельзя связать цепочкой; каждый вызов OrderBy () сортирует по одной проекции собранного типа. Вы все равно могли бы заставить его работать, если бы используемый алгоритм сортировки был стабильным, как MergeSort, но встроенный помощник сортировки является нестабильным QuickSort, поэтому нет гарантии поддержания относительного порядка при сортировке по равным условиям. Объединение в цепочку OrderBy () также будет запускать функцию O (nlogn) один раз для каждого OrderBy (); этот набор методов сортируется один раз, независимо от того, сколько терминов вы сравниваете.

KeithS 20.10.2010 18:34

Это можно улучшить, отказавшись от ToArray (). Хотя OrderBy нельзя связать цепочкой, вы должны иметь возможность связать все Comparers в IComparer<T>, который вы передаете одному OrderBy, верно?

scobi 22.10.2010 22:44

Ну, сортировка (ЛЮБОЙ алгоритм) требует знания всего Enumerable, потому что самым последним элементом может быть тот, который идет первым в упорядоченной коллекции. OrderBy (), за кулисами, в основном делает то, что я делаю здесь; превратить исходный Enumerable в конечную коллекцию, отсортировать его, а затем передать через него.

KeithS 22.10.2010 23:54

Дело принято. :) Также: чем этот набор классов отличается от типов Enumerable.ThenBy () и IOrderedEnumerable в .NET Framework?

scobi 23.10.2010 00:00

На самом деле это не так, как написано. Я не знал о существовании ThenBy (), когда впервые написал это. Однако есть некоторые преимущества. Поскольку это индивидуальный подход, он более расширяемый; например, вы можете добавить методы OrderUsing () и ThenUsing (), которые позволяют передавать Comparison вместо простой проекции (позволяя сортировку на основе элементов, не поддерживающих IComparable). Вы также можете настроить сортировку производительности, заменив Array.Sort, например, параллельным MergeSort.

KeithS 23.10.2010 00:07

Этот не полностью испечен, так как мы только что придумали его сегодня утром. Он сгенерирует полное определение класса для Type. Полезно в ситуациях, когда у вас большой класс и вы хотите создать подмножество или полное определение, но не имеете к нему доступа. Например, чтобы сохранить объект в базе данных и т. д.

public static class TypeExtensions
{
    public static string GenerateClassDefinition(this Type type)
    {
        var properties = type.GetFields();
        var sb = new StringBuilder();
        var classtext = @"private class $name
        {
         $props}";

        foreach (var p in GetTypeInfo(type))
        {
            sb.AppendFormat("  public {0} {1} ", p.Item2, p.Item1).AppendLine(" { get; set; }");
        }

        return classtext.Replace("$name", type.Name).Replace("$props", sb.ToString());
    }

    #region Private Methods
    private static List<Tuple<string, string>> GetTypeInfo(Type type)
    {
        var ret = new List<Tuple<string, string>>();
        var fields = type.GetFields();
        var props = type.GetProperties();

        foreach(var p in props) ret.Add(new Tuple<string, string>(p.Name, TranslateType(p.PropertyType)));    
        foreach(var f in fields) ret.Add(new Tuple<string, string>(f.Name, TranslateType(f.FieldType)));

        return ret;
    }


    private static string TranslateType(Type input)
    {
        string ret;

        if (Nullable.GetUnderlyingType(input) != null)
        {
            ret = string.Format("{0}?", TranslateType(Nullable.GetUnderlyingType(input)));
        }
        else
        {
            switch (input.Name)
            {
                case "Int32": ret = "int"; break;
                case "Int64": ret = "long"; break;
                case "IntPtr": ret = "long"; break;
                case "Boolean": ret = "bool"; break;
                case "String":
                case "Char":
                case "Decimal":
                    ret = input.Name.ToLower(); break;
                default: ret = input.Name; break;
            }
        }

        return ret;
    }
    #endregion
}

Пример использования:

Process.GetProcesses().First().GetType().GenerateClassDefinition();

Становится еще удобнее при использовании linqpad:

Process.GetProcesses().First().GetType().GenerateClassDefinition().Dump();

Спасибо, Джим. Продолжайте в том же духе, конструктивные комментарии, очень полезно.

John 23.10.2010 00:28

@ Джон: нет проблем. Вот несколько проблем: вы создаете частный класс с общедоступными методами доступа. StringBuilder имеет метод AppendFormat, который использует средства форматирования индексов точно так же, как String.Format. В любом случае вы не должны вызывать такие заполнители шаблонов Replace on String. Вы вводите неявную строку. Единственная причина преобразовать struct.Name в псевдоним - удобочитаемость, даже тогда это не имеет особого смысла. Кроме того, IntPtr зависит от платформы, поэтому этот код работает только с 64-разрядной версией. Код, сгенерированный этим, не будет отражать фактическим кодом, который вы пытаетесь сгенерировать.

Jim Schubert 23.10.2010 05:28

Также прошу прощения за лаконичный ответ - я был на работе. Вот отличная глава О'Рейли об размышлениях, которая, я думаю, поможет вам лучше изучить типы, для которых вы пытаетесь имитировать код: oreilly.com/catalog/progcsharp/chapter/ch18.html

Jim Schubert 23.10.2010 05:33

Должен ли частный класс не иметь средств доступа с большей видимостью, чем частный? Я бы предпочел заставить программиста изменить видимость, чем предполагать общедоступность или что-то еще. Я понимаю, что существует appendformat, и я использую его довольно часто, но мне также нужна новая строка, и, к сожалению, у нас нет и AppendFormatLine.

John 23.10.2010 05:56

Обычно я думаю, что вы полностью упускаете суть. Он не предназначен для использования в том виде, в каком он есть. Скорее всего, он будет использоваться на более индивидуальной основе. Например, у вас есть большой объект, который вы хотите представить, либо из БД, либо из класса, у которого нет доступа к источнику. Вы запускаете linqpad, чтобы сгенерировать класс, делаете несколько модов и вперед.

John 23.10.2010 05:58

Кстати: Я считаю, что если у вас нет времени экстраполировать свое негативное мнение, вы должны держать его при себе до тех пор, пока вы это не сделаете. Еще раз спасибо - Джон

John 23.10.2010 06:08
Should a private class not have any accessors with more visibility than private? Нет. Если этот класс не является вложенным, чего не предлагает ваш код, вы получите ошибку Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal. Я могу понять, как быстро генерировать классы для соответствия db, я делаю это сам. Кроме того, StringBuilder имеет AppendFormat() и AppendLine();. Просто вызовите их оба или напишите метод расширения под названием AppendFormatLine.
Jim Schubert 23.10.2010 09:13

«Нет. Если только этот класс не является вложенным, что не предлагает ваш код». Мой код ничего не предлагает. Он просто генерирует класс и заставляет программиста решить, какой должна быть доступность сгенерированного класса. «StringBuilder имеет AppendFormat () и AppendLine (); просто вызовите их оба или напишите метод расширения». Это один из способов решения этой проблемы. Я также мог бы использовать string.Format внутри AppendLine.

John 23.10.2010 16:15

«Вы вводите строку неявно. Единственная причина для преобразования struct.Name в псевдоним - удобочитаемость, даже если это не имеет особого смысла». Пожалуйста, объясните это дальше. Вы хотите сказать, что я должен оставить его как «String», а не «string»? «Кроме того, IntPtr зависит от платформы, поэтому этот код работает только с 64-разрядной версией». Все хорошо, но код будет работать в 32-битном формате. Я нигде не использую IntPtr. Я просто представляю это как длинный, если он пройдет. Как и в приведенном мной примере.

John 23.10.2010 16:40

Строка неявного типа с использованием ключевого слова var заставляет компилятор определять тип этой переменной. Если вы знаете, что это строка, почему бы не назвать ее строкой, чтобы компилятору не пришлось делать ничего лишнего? Комментарий о преобразовании struct.Name в псевдоним означает, что вы берете фактическую структуру из BCL (например, Int32) и переименовываете ее в строку, используя ее псевдоним (например, int). В этом нет ничего плохого, но вы можете так же легко вызвать int «Int32», длинный «Int64» или IntPtr «IntPtr». Изменив имя на псевдоним, вы ввели 64-битную ошибку.

Jim Schubert 23.10.2010 20:08

Что касается вашего комментария о доступности на уровне класса, по умолчанию закрытой: доступ по умолчанию для класса, если вы не укажете модификатор доступа, будет internal. Я хочу сказать, что я не понимаю, почему вы «заставляете разработчика» что-то делать, генерируя код, который не компилируется ... почему бы не отказаться от модификатора доступа? Разве это не соглашение, используемое, когда Visual Studio создает для вас заглушку класса? Используя частную переменную, вы неизбежно вызовете путаницу.

Jim Schubert 23.10.2010 20:16

Кроме того, если вы обнаружите, что используете этот код довольно часто, возможно, стоит выложить 125 долларов и .NET Reflector Pro: red-gate.com/products/reflector Затем вы сможете проанализировать внутреннее устройство точный большинства библиотек .NET. Я уверен, что вы уже слышали об этом, но другие, читающие эту ветку, могут не слышать.

Jim Schubert 23.10.2010 20:20

Моя работа не в том, чтобы облегчить работу компилятору. Задача компилятора - облегчить мне жизнь. Я использую его, когда это возможно, если это имеет смысл (мы часто используем var на работе, и это многократно окупалось). На мой взгляд, использование var для строк совершенно нормально. Я мог бы легко оставить типы такими, какими они являются, но поскольку это предназначено для создания класса, который пользователь затем включит в свой код, он должен быть написан так, как они. Используете ли вы в своем коде «Int32 x = 5»? IntPtr не является ошибкой, как я уже говорил, он будет работать на 32-битной версии.

John 23.10.2010 20:22

Я хорошо осведомлен о преимуществах рефлектора. Мы постоянно пользуемся бесплатной версией. В профессиональной версии нет ничего полезного. Что касается частной части, вы снова упускаете суть кода. Я просто оставлю все как есть, так как это никуда не денется.

John 23.10.2010 20:50

Вот еще одна пара, которой я нашел бесконечное применение:

public static T ObjectWithMin<T, TResult>(this IEnumerable<T> sequence, Func<T, TResult> predicate)
    where T : class
    where TResult : IComparable
{
    if (!sequence.Any()) return null;

    //get the first object with its predicate value
    var seed = sequence.Select(x => new {Object = x, Value = predicate(x)}).FirstOrDefault();
    //compare against all others, replacing the accumulator with the lesser value
    //tie goes to first object found
    return
        sequence.Select(x => new {Object = x, Value = predicate(x)})
            .Aggregate(seed,(acc, x) => acc.Value.CompareTo(x.Value) <= 0 ? acc : x).Object;
}

public static T ObjectWithMax<T, TResult>(this IEnumerable<T> sequence, Func<T, TResult> predicate)
    where T : class
    where TResult : IComparable
{
    if (!sequence.Any()) return null;

    //get the first object with its predicate value
    var seed = sequence.Select(x => new {Object = x, Value = predicate(x)}).FirstOrDefault();
    //compare against all others, replacing the accumulator with the greater value
    //tie goes to last object found
    return
        sequence.Select(x => new {Object = x, Value = predicate(x)})
            .Aggregate(seed, (acc, x) => acc.Value.CompareTo(x.Value) > 0 ? acc : x).Object;
}

Использование:

var myObject = myList.ObjectWithMin(x=>x.PropA);

Эти методы в основном заменяют такие обычаи, как

var myObject = myList.OrderBy(x=>x.PropA).FirstOrDefault(); //O(nlog(n)) and unstable

и

var myObject = myList.Where(x=>x.PropA == myList.Min(x=>x.PropA)).FirstOrDefault(); //O(N^2) but stable

и

var minValue = myList.Min(x=>x.PropA);
var myObject = myList.Where(x=>x.PropA == minValue).FirstOrDefault(); //not a one-liner, and though linear and stable it's slower (evaluates the enumerable twice)

И еще одно:

public enum ParseFailBehavior
{
   ReturnNull,
   ReturnDefault,
   ThrowException
}

public static T? ParseNullableEnum<T>(this string theValue, ParseFailBehavior desiredBehavior = ParseFailBehavior.ReturnNull) where T:struct
{
   T output;
   T? result = Enum.TryParse<T>(theValue, out output) 
      ? (T?)output
      : desiredBehavior == ParseFailBehavior.ReturnDefault
         ? (T?)default(T)
         : null;

   if (result == null && desiredBehavior == ParseFailBehavior.ThrowException)
      throw new ArgumentException("Parse Failed for value {0} of enum type {1}".
         FormatWith(theValue, typeof(T).Name));       
}

Для этой версии требуется .NET 4.0; в 3.5 у вас нет TryParse и необязательных параметров; вы застряли с Enum.Parse (), который вам нужно попробовать. Это по-прежнему полностью выполнимо в версии 3.5 (и намного более полезно, поскольку Enum.Parse () унылый и ваш единственный другой вариант):

public static T? ParseNummableEnum<T>(this string theValue)
{
    return theValue.ParseNullableEnum<T>(ParseFailBehavior.ReturnNull);
}

public static T? ParseNullableEnum<T>(this string theValue, 
    ParseFailBehavior desiredBehavior) where T:struct
{
    try
    {
        return (T?) Enum.Parse(typeof (T), theValue);
    }
    catch (Exception)
    {
        if (desiredBehavior == ParseFailBehavior.ThrowException) throw;
    }

    return desiredBehavior == ParseFailBehavior.ReturnDefault ? (T?)default(T) : null;
}

Использование:

//returns null if OptionOne isn't an enum constant
var myEnum = "OptionOne".ParseNullableEnum<OptionEnum>(); 

//guarantees a return value IF the enum has a "zero" constant value (generally a good practice)
var myEnum = "OptionTwo".ParseNullableEnum<OptionEnum>(ParseFailBehavior.ReturnDefault).Value 

Вот несколько методов, которые я использую, чтобы сделать извлечение отдельных атрибутов менее болезненным:

public static T GetAttribute<T>(this ICustomAttributeProvider provider, bool inherit = false, int index = 0) where T : Attribute
{
    return provider.GetAttribute(typeof(T), inherit, index) as T;
}

public static Attribute GetAttribute(this ICustomAttributeProvider provider, Type type, bool inherit = false, int index = 0)
{
    bool exists = provider.IsDefined(type, inherit);
    if (!exists)
    {
        return null;
    }

    object[] attributes = provider.GetCustomAttributes(type, inherit);
    if (attributes != null && attributes.Length != 0)
    {
        return attributes[index] as Attribute;
    }
    else
    {
        return null;
    }
}

Использование (реализация взлома описания перечисления это):

public static string GetDescription(this Enum value)
{
    var fieldInfo = value.GetType().GetField(value.ToString());
    var attribute = fieldInfo.GetAttribute<DescriptionAttribute>();
    return attribute != null ? attribute.Description : null;
}

Не стесняйтесь включать это в проект CodePlex!

Стиль Smalltalk if / else в C#.

Не стесняйтесь помещать это в codeplex под любой лицензией, которую вы используете

using System;
namespace SmalltalkBooleanExtensionMethods
{

    public static class BooleanExtension
    {
        public static T ifTrue<T> (this bool aBoolean, Func<T> method)
        {
        if (aBoolean)
            return (T)method();
        else
            return default(T);
    }

        public static void ifTrue (this bool aBoolean, Action method)
        {
            if (aBoolean)
                method();
        }


        public static T ifFalse<T> (this bool aBoolean, Func<T> method)
        {
            if (!aBoolean)
                return (T)method();
            else
                return default(T);
        }

        public static void ifFalse (this bool aBoolean, Action method)
        {
            if (!aBoolean)
                method();
        }


        public static T ifTrueifFalse<T> (this Boolean aBoolean, Func<T> methodA, Func<T> methodB)
        {
            if (aBoolean)
                return (T)methodA();
            else
                return (T)methodB();
        }

        public static void ifTrueifFalse (this Boolean aBoolean, Action methodA, Action methodB)
        {
            if (aBoolean)
                methodA();
            else
                methodB();
        }

    }


}

У вас, вероятно, уже есть метод timesRepeat, но он там.

using System;

namespace SmalltalkBooleanExtensionMethods
{
    public static class IntExtension
    {
        public static int timesRepeat<T>(this int x, Func<T> method)
        {
            for (int i = x; i > 0; i--)
            {
                method();
            }

            return x;
        }

        public static int timesRepeat(this int x, Action method)
        {
            for (int i = x; i > 0; i--)
            {
                method();
            }

            return x;
        }
    }
}

Nunit тесты

using System;
using SmalltalkBooleanExtensionMethods;
using NUnit.Framework;

namespace SmalltalkBooleanExtensionMethodsTest
{
    [TestFixture]
    public class SBEMTest
    {
        int i;
        bool itWorks;

        [SetUp]
        public void Init()
        {

            i = 0;
            itWorks = false;
        }

        [Test()]
        public void TestifTrue()
        {

            itWorks = (true.ifTrue(() => true));
            Assert.IsTrue(itWorks);
        }
        [Test()]
        public void TestifFalse()
        {
            itWorks = (false.ifFalse(() => true));
            Assert.IsTrue(itWorks);
        }

        [Test()]
        public void TestifTrueifFalse()
        {
            itWorks = false.ifTrueifFalse(() => false, () => true);
            Assert.IsTrue(itWorks);
            itWorks = false;
            itWorks = true.ifTrueifFalse(() => true, () => false);
            Assert.IsTrue(itWorks);
        }

        [Test()]
        public void TestTimesRepeat()
        {
            (5).timesRepeat(() => i = i + 1);
            Assert.AreEqual(i, 5);
        }

        [Test()]
        public void TestVoidMethodIfTrue()
        {

            true.ifTrue(() => SetItWorksBooleanToTrue());
            Assert.IsTrue(itWorks);
        }

        [Test()]
        public void TestVoidMethodIfFalse()
        {

            false.ifFalse(() => SetItWorksBooleanToTrue());
            Assert.IsTrue(itWorks);
        }

        public void TestVoidMethodIfTrueIfFalse()
        {
            true.ifTrueifFalse(() => SetItWorksBooleanToTrue(), () => SetItWorksBooleanToFalse());
            false.ifTrueifFalse(() => SetItWorksBooleanToFalse(), () => SetItWorksBooleanToTrue());
            Assert.IsTrue(itWorks);

        }

        public void TestVoidMethodTimesRepeat()
        {
            (5).timesRepeat(() => AddOneToi());
            Assert.AreEqual(i, 5);
        }

        public void SetItWorksBooleanToTrue()
        {
            itWorks = true;
        }

        public void SetItWorksBooleanToFalse()
        {
            itWorks = false;
        }

        public void AddOneToi()
        {
            i = i + 1;
        }
    }
}

Почему вы используете method.DynamicInvoke()? Прямой вызов вроде этого: method() намного быстрее. stackoverflow.com/questions/932699/…

Greg 14.11.2010 22:23

Материал, который я видел, показывающий, как вызвать лямбду, использовал .DynamicInvoke (), я не был уверен, что есть лучший способ.

Roman A. Taycher 15.11.2010 08:30

Вызов DynamicInvoke () - единственное, что я сделал не так, или что-то еще? Если это было что-то еще, мне любопытно, что еще вызвало отрицательные голоса. Я ценю объяснение вызова и буду рад услышать все, что я ошибся.

Roman A. Taycher 15.11.2010 09:13

Я не уверен, почему это получило пару голосов против, я не вижу ничего плохого в самом коде. Может быть, те, кто проголосовал против, думали, что эти методы не совсем подходят для C#. Вы объясняете, что заимствуете стиль Smalltalk, поэтому я не вижу, что в этом плохого.

Greg 30.11.2010 06:54

Я не ожидаю использовать ifTrue / ifFalse в реальном коде, это было просто интересно реализовать.

Roman A. Taycher 06.12.2010 12:10

Нормализация пробелов довольно полезна, особенно при работе с пользовательским вводом:

namespace Extensions.String
{
    using System.Text.RegularExpressions;

    public static class Extensions
    {
        /// <summary>
        /// Normalizes whitespace in a string.
        /// Leading/Trailing whitespace is eliminated and
        /// all sequences of internal whitespace are reduced to
        /// a single SP (ASCII 0x20) character.
        /// </summary>
        /// <param name = "s">The string whose whitespace is to be normalized</param>
        /// <returns>a normalized string</returns>
        public static string NormalizeWS( this string @this )
        {
            string src        = @this ?? "" ;
            string normalized = rxWS.Replace( src , m =>{
                  bool isLeadingTrailingWS = ( m.Index == 0 || m.Index+m.Length == src.Length ? true : false ) ;
                  string p                 = ( isLeadingTrailingWS ? "" : " " ) ;
                  return p ;
                }) ;

            return normalized ;

        }
        private static Regex rxWS = new Regex( @"\s+" ) ;
    }
}

Я создал метод расширения для выбора элемента в раскрывающемся списке ASP.NET.

Ниже приведен код

 public static class Utilities
{
    public enum DropDownListSelectionType
    {
        ByValue,
        ByText
    }

    public static void SelectItem(this  System.Web.UI.WebControls.DropDownList drp, string selectedValue, DropDownListSelectionType type)
    {
        drp.ClearSelection();
        System.Web.UI.WebControls.ListItem li;
        if (type == DropDownListSelectionType.ByValue)
            li = drp.Items.FindByValue(selectedValue.Trim());
        else
            li = drp.Items.FindByText(selectedValue.Trim());
        if (li != null)
            li.Selected = true;
    }}

Этот метод может быть вызван следующими строками кода для выбора по тексту

DropDownList1.SelectItem("ABCD", Utilities.DropDownListSelectionType.ByText);

или выберите по значению

DropDownList1.SelectItem("11", Utilities.DropDownListSelectionType.ByValue);

Приведенный выше код ничего не выбирает, если не может найти переданный текст / значение.

Вот расширение для растровых изображений, которое может преобразовывать растровые изображения в оттенки серого;

public static Bitmap GrayScale(this Bitmap bitmap)
{
    Bitmap newBitmap = new Bitmap(bitmap.Width, bitmap.Height);
    Graphics g = Graphics.FromImage(newBitmap);

    //the grayscale ColorMatrix
    ColorMatrix colorMatrix = new ColorMatrix(new float[][] {
            new float[] {.3f, .3f, .3f, 0, 0},
            new float[] {.59f, .59f, .59f, 0, 0},
            new float[] {.11f, .11f, .11f, 0, 0},
            new float[] {0, 0, 0, 1, 0},
            new float[] {0, 0, 0, 0, 1}
    });

    ImageAttributes attributes = new ImageAttributes();
    attributes.SetColorMatrix(colorMatrix);
    g.DrawImage(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height), 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, attributes);
    g.Dispose();
    return newBitmap;
}

Пример использования:

Bitmap grayscaled = bitmap.GrayScale()

Хороший! Знаете ли вы, как сделать то же самое с WPF (без использования GDI)?

Thomas Levesque 14.01.2011 12:42

А вот расширение для вызова управления, которое я использую регулярно;

public static class InvokeExtensions
{
    public static void InvokeHandler(this Control control, MethodInvoker del) // Sync. control-invoke extension.
    {
        if (control.InvokeRequired)
        {
            control.Invoke(del);
            return; 
        }
        del(); // run the actual code.
    }

    public static void AsyncInvokeHandler(this Control control, MethodInvoker del) // Async. control-invoke extension.
    {
        if (control.InvokeRequired)
        {
            control.BeginInvoke(del);
            return; 
        }
        del(); // run the actual code.
    }
}

образец;

this.TreeView.AsyncInvokeHandler(() =>
        {
            this.Text = 'xyz'
        });

который позволяет обновлять графический интерфейс между потоками.

Может быть, я еще не все ответы проверил.

HuseyinUslu 15.01.2011 12:53

Некоторые инструменты для IEnumerable: ToString (Формат), ToString (Функция) и Соединение (Разделитель).

Например:

var names = new[] { "Wagner", "Francine", "Arthur", "Bernardo" };

names.ToString("Name: {0}\n");
// Name: Wagner
// Name: Francine
// Name: Arthur
// Name: Bernardo

names.ToString(name => name.Length > 6 ? String.Format("{0} ", name) : String.Empty);
// Francine Bernardo

names.Join(" - ");
// Wagner - Francine - Arthur - Bernardo

Расширения:

public static string ToString<T>(this IEnumerable<T> self, string format)
{
    return self.ToString(i => String.Format(format, i));
}

public static string ToString<T>(this IEnumerable<T> self, Func<T, object> function)
{
    var result = new StringBuilder();

    foreach (var item in self) result.Append(function(item));

    return result.ToString();
}

public static string Join<T>(this IEnumerable<T> self, string separator)
{
    return String.Join(separator, values: self.ToArray());
}

Вы можете заменить все в вашем методе Join<T> следующим: return string.Join(seperator, self.ToArray()).

Pondidum 14.01.2011 19:09

Ой, правда, забыл об этом методе. Спасибо, Пондидум! Отредактировано.

19WAS85 14.01.2011 19:16

Я думал, что видел это где-то раньше, но нигде не нашел здесь подсказок. У MS есть функция TryGetValue в интерфейсе IDictionary, но она возвращает логическое значение и дает значение в параметре out, поэтому вот более простая и понятная реализация:

public static TVal GetValueOrDefault<TKey, TVal>(this IDictionary<TKey, TVal> d, TKey key) {
  if (d.ContainsKey(key))
    return d[key];
  return default(TVal);
}

Все TryXxx имеют параметр out и результат bool: в этом весь смысл этого шаблона! Ваш метод должен называться как-то вроде DefaultGetValue, чтобы отличать его от TryGetValue.

Jeroen Wiert Pluimers 17.01.2011 19:47

@Jeroen Согласен, хотя я бы назвал его GetValueOrDefault, чтобы он больше соответствовал другим аналогичным методам, таким как FirstOrDefault

Davy8 17.01.2011 21:48

Хорошо, замечание принято, я переименовал метод, как предложил @Davy8.

Shaul Behr 17.01.2011 22:17

@ Davy8: отличное предложение; @Shaul: спасибо, что исправили (+1).

Jeroen Wiert Pluimers 17.01.2011 22:27

Вот еще одно контрольное расширение, которое я использовал, хотя не знаю, размещалось ли оно здесь раньше.

public static class ControlExtensions
{
    public static void DoubleBuffer(this Control control) 
    {
        // http://stackoverflow.com/questions/76993/how-to-double-buffer-net-controls-on-a-form/77233#77233
        // Taxes: Remote Desktop Connection and painting: http://blogs.msdn.com/oldnewthing/archive/2006/01/03/508694.aspx

        if (System.Windows.Forms.SystemInformation.TerminalServerSession) return;
        System.Reflection.PropertyInfo dbProp = typeof(System.Windows.Forms.Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        dbProp.SetValue(control, true, null);
    }
}

использование:

this.someControl.DoubleBuffer();

Я создал красивое расширение Each, которое имеет то же поведение, что и каждая функция jQuery.

Это позволяет что-то вроде ниже, где вы можете получить индекс текущего значения и выйти из цикла, вернув false:

new[] { "first", "second", "third" }.Each((value, index) =>
{
    if (value.Contains("d"))
        return false;
    Console.Write(value);
    return true;
});

Вот код

/// <summary>
/// Generic iterator function that is useful to replace a foreach loop with at your discretion.  A provided action is performed on each element.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "source"></param>
/// <param name = "action">Function that takes in the current value in the sequence. 
/// <returns></returns>
public static IEnumerable<T> Each<T>(this IEnumerable<T> source, Action<T> action)
{
    return source.Each((value, index) =>
    {
        action(value);
        return true;
    });
}


/// <summary>
/// Generic iterator function that is useful to replace a foreach loop with at your discretion.  A provided action is performed on each element.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "source"></param>
/// <param name = "action">Function that takes in the current value and its index in the sequence.  
/// <returns></returns>
public static IEnumerable<T> Each<T>(this IEnumerable<T> source, Action<T, int> action)
{
    return source.Each((value, index) =>
    {
        action(value, index);
        return true;
    });
}

/// <summary>
/// Generic iterator function that is useful to replace a foreach loop with at your discretion.  A provided action is performed on each element.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "source"></param>
/// <param name = "action">Function that takes in the current value in the sequence.  Returns a value indicating whether the iteration should continue.  So return false if you don't want to iterate anymore.</param>
/// <returns></returns>
public static IEnumerable<T> Each<T>(this IEnumerable<T> source, Func<T, bool> action)
{
    return source.Each((value, index) =>
    {
        return action(value);
    });
}

/// <summary>
/// Generic iterator function that is useful to replace a foreach loop with at your discretion.  A provided action is performed on each element.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "source"></param>
/// <param name = "action">Function that takes in the current value and its index in the sequence.  Returns a value indicating whether the iteration should continue.  So return false if you don't want to iterate anymore.</param>
/// <returns></returns>
public static IEnumerable<T> Each<T>(this IEnumerable<T> source, Func<T, int, bool> action)
{
    if (source == null)
        return source;

    int index = 0;
    foreach (var sourceItem in source)
    {
        if (!action(sourceItem, index))
            break;
        index++;
    }
    return source;
}

Чем он отличается от TakeWhile? (кроме перегрузок с Action)

Thomas Levesque 19.05.2011 02:43

Сравните равенство двух объектов без (обязательно) переопределения Equals или реализации IEquatable <>.

Зачем вам это нужно? Когда вы действительно хотите узнать, равны ли два объекта, но вам лень переопределить Equals(object) или реализовать IEquatable<T>. Или, что более реалистично, если у вас ужасно сложный класс, и реализация Equals вручную будет чрезвычайно утомительной, подверженной ошибкам и неинтересной в обслуживании. Это также помогает, если вы не слишком заботитесь о производительности.

В настоящее время я использую IsEqualTo по второй причине - у меня есть класс со многими свойствами, типы которых являются другими определяемыми пользователем классами, каждый из которых имеет много других свойств, типы которых являются другими определяемыми пользователем классами, до бесконечности. Добавьте кучу коллекций во многие из этих классов, и реализация Equals(object) поистине станет кошмаром.

Использование:

if (myTerriblyComplexObject.IsEqualTo(myOtherTerriblyComplexObject))
{
    // Do something terribly interesting.
}

Чтобы определить равенство, я провожу многочисленные сравнения. Я изо всех сил стараюсь сделать «правильное» в «правильном» порядке. Сравнения по порядку:

  1. Используйте статический метод Equals(object, object). Если он вернет истину, верните истину. Он вернет истину, если ссылки совпадают. Он также вернет истину, если thisObject переопределяет Equals(object).
  2. Если thisObject равен нулю, вернуть ложь. Если он равен нулю, дальнейшие сравнения невозможны.
  3. Если thisObject переопределил Equals(object), вернуть false. Поскольку он переопределяет Equals, это должно означать, что Equals был выполнен на шаге № 1 и вернул false. Если кто-то потрудился переопределить Equals, мы должны уважать это и возвращать то, что Equals возвращает.
  4. Если thisObject наследуется от IEquatable<T>, где otherObject может быть назначен на T, получите метод Equals(T) с использованием отражения. Вызвать этот метод и вернуть его возвращаемое значение.
  5. Если оба объекта - IEnumerable, верните, содержат ли одни и те же элементы в том же порядке, используя IsEqualTo для сравнения элементов.
  6. Если объекты имеют разные типы, верните false. Поскольку теперь мы знаем, что thisObject не имеет метода Equals, нет никакого способа реально оценить истинность двух объектов разных типов.
  7. Если объекты являются типом значения (примитивом или структурой) или строкой, верните false. Мы уже провалили тест Equals(object) - этого достаточно.
  8. Для каждого свойства thisObject проверьте его значение с помощью IsEqualTo. Если какой-либо из них возвращает false, верните false. Если все вернут истину, верните истину.

Сравнение строк могло бы быть лучше, но его легко реализовать. Кроме того, я не на 100% уверен, что правильно обрабатываю структуры.

Без лишних слов, вот метод расширения:

/// <summary>
/// Provides extension methods to determine if objects are equal.
/// </summary>
public static class EqualsEx
{
    /// <summary>
    /// The <see cref = "Type"/> of <see cref = "string"/>.
    /// </summary>
    private static readonly Type StringType = typeof(string);

    /// <summary>
    /// The <see cref = "Type"/> of <see cref = "object"/>.
    /// </summary>
    private static readonly Type ObjectType = typeof(object);

    /// <summary>
    /// The <see cref = "Type"/> of <see cref = "IEquatable{T}"/>.
    /// </summary>
    private static readonly Type EquatableType = typeof(IEquatable<>);

    /// <summary>
    /// Determines whether <paramref name = "thisObject"/> is equal to <paramref name = "otherObject"/>.
    /// </summary>
    /// <param name = "thisObject">
    /// This object.
    /// </param>
    /// <param name = "otherObject">
    /// The other object.
    /// </param>
    /// <returns>
    /// True, if they are equal, otherwise false.
    /// </returns>
    public static bool IsEqualTo(this object thisObject, object otherObject)
    {
        if (Equals(thisObject, otherObject))
        {
            // Always check Equals first. If the object has overridden Equals, use it. This will also capture the case where both are the same reference.
            return true;
        }

        if (thisObject == null)
        {
            // Because Equals(object, object) returns true if both are null, if either is null, return false.
            return false;
        }

        var thisObjectType = thisObject.GetType();
        var equalsMethod = thisObjectType.GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance, null, new[] { ObjectType }, null);
        if (equalsMethod.DeclaringType == thisObjectType)
        {
            // thisObject overrides Equals, and we have already failed the Equals test, so return false.
            return false;
        }

        var otherObjectType = otherObject == null ? null : otherObject.GetType();

        // If thisObject inherits from IEquatable<>, and otherObject can be passed into its Equals method, use it.
        var equatableTypes = thisObjectType.GetInterfaces().Where(                                          // Get interfaces of thisObjectType that...
            i => i.IsGenericType                                                                            // ...are generic...
            && i.GetGenericTypeDefinition() == EquatableType                                                // ...and are IEquatable of some type...
            && (otherObjectType ==  null || i.GetGenericArguments()[0].IsAssignableFrom(otherObjectType))); // ...and otherObjectType can be assigned to the IEquatable's type.

        if (equatableTypes.Any())
        {
            // If we found any interfaces that meed our criteria, invoke the Equals method for each interface.
            // If any return true, return true. If all return false, return false.
            return equatableTypes
                .Select(equatableType => equatableType.GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance))
                .Any(equatableEqualsMethod => (bool)equatableEqualsMethod.Invoke(thisObject, new[] { otherObject }));
        }

        if (thisObjectType != StringType && thisObject is IEnumerable && otherObject is IEnumerable)
        {
            // If both are IEnumerable, check their items.
            var thisEnumerable = ((IEnumerable)thisObject).Cast<object>();
            var otherEnumerable = ((IEnumerable)otherObject).Cast<object>();

            return thisEnumerable.SequenceEqual(otherEnumerable, IsEqualToComparer.Instance);
        }

        if (thisObjectType != otherObjectType)
        {
            // If they have different types, they cannot be equal.
            return false;
        }

        if (thisObjectType.IsValueType || thisObjectType == StringType)
        {
            // If it is a value type, we have already determined that they are not equal, so return false.
            return false;
        }

        // Recurse into each public property: if any are not equal, return false. If all are true, return true.
        return !(from propertyInfo in thisObjectType.GetProperties()
                 let thisPropertyValue = propertyInfo.GetValue(thisObject, null)
                 let otherPropertyValue = propertyInfo.GetValue(otherObject, null)
                 where !thisPropertyValue.IsEqualTo(otherPropertyValue)
                 select thisPropertyValue).Any();
    }

    /// <summary>
    /// A <see cref = "IEqualityComparer{T}"/> to be used when comparing sequences of collections.
    /// </summary>
    private class IsEqualToComparer : IEqualityComparer<object>
    {
        /// <summary>
        /// The singleton instance of <see cref = "IsEqualToComparer"/>.
        /// </summary>
        public static readonly IsEqualToComparer Instance;

        /// <summary>
        /// Initializes static members of the <see cref = "EqualsEx.IsEqualToComparer"/> class.
        /// </summary>
        static IsEqualToComparer()
        {
            Instance = new IsEqualToComparer();
        }

        /// <summary>
        /// Prevents a default instance of the <see cref = "EqualsEx.IsEqualToComparer"/> class from being created.
        /// </summary>
        private IsEqualToComparer()
        {
        }

        /// <summary>
        /// Determines whether the specified objects are equal.
        /// </summary>
        /// <param name = "x">
        /// The first object to compare.
        /// </param>
        /// <param name = "y">
        /// The second object to compare.
        /// </param>
        /// <returns>
        /// true if the specified objects are equal; otherwise, false.
        /// </returns>
        bool IEqualityComparer<object>.Equals(object x, object y)
        {
            return x.IsEqualTo(y);
        }

        /// <summary>
        /// Not implemented - throws an <see cref = "NotImplementedException"/>.
        /// </summary>
        /// <param name = "obj">
        /// The <see cref = "object"/> for which a hash code is to be returned.
        /// </param>
        /// <returns>
        /// A hash code for the specified object.
        /// </returns>
        int IEqualityComparer<object>.GetHashCode(object obj)
        {
            throw new NotImplementedException();
        }
    }
}

Сервер SQL имеет ограничение в ~ 2000 параметров, что является проблемой, если у вас есть идентификаторы 10k и вы хотите, чтобы записи были связаны с ними. Я написал эти методы, которые принимают пакетные списки идентификаторов и называются так:

List<Order> orders = dataContext.Orders.FetchByIds(
  orderIdChunks,
  list => row => list.Contains(row.OrderId)
);

List<Customer> customers = dataContext.Orders.FetchByIds(
  orderIdChunks,
  list => row => list.Contains(row.OrderId),
  row => row.Customer
);

public static List<ResultType> FetchByIds<RecordType, ResultType>(
    this IQueryable<RecordType> querySource,
    List<List<int>> IdChunks,
    Func<List<int>, Expression<Func<RecordType, bool>>> filterExpressionGenerator,
    Expression<Func<RecordType, ResultType>> projectionExpression
    ) where RecordType : class
{
    List<ResultType> result = new List<ResultType>();
    foreach (List<int> chunk in IdChunks)
    {
        Expression<Func<RecordType, bool>> filterExpression =
            filterExpressionGenerator(chunk);

        IQueryable<ResultType> query = querySource
            .Where(filterExpression)
            .Select(projectionExpression);

        List<ResultType> rows = query.ToList();
        result.AddRange(rows);
    }

    return result;
}

public static List<RecordType> FetchByIds<RecordType>(
    this IQueryable<RecordType> querySource,
    List<List<int>> IdChunks,
    Func<List<int>, Expression<Func<RecordType, bool>>> filterExpressionGenerator
    ) where RecordType : class
{
    Expression<Func<RecordType, RecordType>> identity = r => r;

    return FetchByIds(
        querySource,
        IdChunks,
        filterExpressionGenerator,
        identity
        );
}

Я искал способ внести свой вклад в сообщество некоторых вещей, которые я разработал.

Вот несколько расширений FileInfo, которые я считаю весьма полезными.

/// <summary>
/// Open with default 'open' program
/// </summary>
/// <param name = "value"></param>
public static Process Open(this FileInfo value)
{
    if (!value.Exists)
        throw new FileNotFoundException("File doesn't exist");
    Process p = new Process();
    p.StartInfo.FileName = value.FullName;
    p.StartInfo.Verb = "Open";
    p.Start();
    return p;
}

/// <summary>
/// Print the file
/// </summary>
/// <param name = "value"></param>
public static void Print(this FileInfo value)
{
    if (!value.Exists)
        throw new FileNotFoundException("File doesn't exist");
    Process p = new Process();
    p.StartInfo.FileName = value.FullName;
    p.StartInfo.Verb = "Print";
    p.Start();
}

/// <summary>
/// Send this file to the Recycle Bin
/// </summary>
/// <exception cref = "File doesn't exist" />
/// <param name = "value"></param>
public static void Recycle(this FileInfo value)
{        
    value.Recycle(false);
}

/// <summary>
/// Send this file to the Recycle Bin
/// On show, if person refuses to send file to the recycle bin, 
/// exception is thrown or otherwise delete fails
/// </summary>
/// <exception cref = "File doesn't exist" />
/// <exception cref = "On show, if user refuses, throws exception 'The operation was canceled.'" />
/// <param name = "value">File being recycled</param>
/// <param name = "showDialog">true to show pop-up</param>
public static void Recycle(this FileInfo value, bool showDialog)
{
    if (!value.Exists)
            throw new FileNotFoundException("File doesn't exist");
    if ( showDialog )
        FileSystem.DeleteFile
            (value.FullName, UIOption.AllDialogs, 
            RecycleOption.SendToRecycleBin);
    else
        FileSystem.DeleteFile
            (value.FullName, UIOption.OnlyErrorDialogs, 
            RecycleOption.SendToRecycleBin);
}

Откройте любой файл в любимом редакторе пользователя:

new FileInfo("C:\image.jpg").Open();

Распечатайте любой файл, который операционная система умеет печатать:

new FileInfo("C:\image.jpg").Print();

Отправьте любой файл в корзину:

  1. Вы должны включить ссылку Microsoft.VisualBasic
  2. используйте using Microsoft.VisualBasic.FileIO;

Пример:

new FileInfo("C:\image.jpg").Recycle();

Или же

// let user have a chance to cancel send to recycle bin.
new FileInfo("C:\image.jpg").Recycle(true);

Я не знаю, как назвать это Open, но это все еще хорошие расширения, и я удивлен, что их раньше никто не публиковал.

leviathanbadger 21.03.2012 19:13

У нас есть инструмент развертывания для развертывания между средами. Поскольку файлы можно было пометить как измененные, но на самом деле не разные, я придумал следующее:

/// <summary>
/// Compares the files to see if they are different. 
/// First checks file size
/// Then modified if the file is larger than the specified size
/// Then compares the bytes
/// </summary>
/// <param name = "file1">The source file</param>
/// <param name = "file2">The destination file</param>
/// <param name = "mb">Skip the smart check if the file is larger than this many megabytes. Default is 10.</param>
/// <returns></returns>
public static bool IsDifferentThan(this FileInfo file1, FileInfo file2, int mb = 10)
{
  var ret = false;

  // different size is a different file
  if (file1.Length != file2.Length) return true;

  // if the file times are different and the file is bigger than 10mb flag it for updating
  if (file1.LastWriteTimeUtc > file2.LastWriteTimeUtc && file1.Length > ((mb*1024)*1024)) return true;

  var f1 = File.ReadAllBytes(file1.FullName);
  var f2 = File.ReadAllBytes(file2.FullName);

  // loop through backwards because if they are different
  // it is more likely that the last few bytes will be different
  // than the first few
  for(var i = file1.Length - 1; i > 0; i--)
  {
    if (f1[i] != f2[i])
    {
      ret = true;
      break;
    }
  }

  return ret;
}

Два цветовых расширения, которые я использую, в основном, для разработки элементов управления:

public static class ColorExtensions
{
  // Gets a color that will be readable on top of a given background color
  public static Color GetForegroundColor(this Color input)
  {
    // Math taken from one of the replies to
    // http://stackoverflow.com/questions/2241447/make-foregroundcolor-black-or-white-depending-on-background
    if (Math.Sqrt(input.R * input.R * .241 + input.G * input.G * .691 + input.B * input.B * .068) > 128)
      return Color.Black;
    else
      return Color.White;
  }

  // Converts a given Color to gray
  public static Color ToGray(this Color input)
  {
    int g = (int)(input.R * .299) + (int)(input.G * .587) + (int)(input.B * .114);
    return Color.FromArgb(input.A, g, g, g);
  }
}

Использование:

Color foreColor = someBackColor.GetForegroundColor();
Color grayColor = someBackColor.ToGray();

Используйте отражение, чтобы найти метод TryParse и вызвать его для строковой цели. Необязательный параметр указывает, что должно быть возвращено в случае сбоя преобразования. Я считаю этот метод весьма полезным в большинстве случаев. Хорошо знаком с опцией Convert.ChangeType, но я считаю ее более полезной, что удобно с результатом по умолчанию и еще много чего. Обратите внимание, что найденные методы хранятся в словаре, хотя я подозреваю, что бокс в конечном итоге немного замедляет это.

Этот метод - мой любимый, потому что он законно использует множество языковых функций.

private static readonly Dictionary<Type, MethodInfo> Parsers = new Dictionary<Type, MethodInfo>();

public static T Parse<T>(this string value, T defaultValue = default(T))
{
    if (string.IsNullOrEmpty(value)) return defaultValue;

    if (!Parsers.ContainsKey(typeof(T)))
        Parsers[typeof (T)] = typeof (T).GetMethods(BindingFlags.Public | BindingFlags.Static)
            .Where(mi => mi.Name == "TryParse")
            .Single(mi =>
                        {
                            var parameters = mi.GetParameters();
                            if (parameters.Length != 2) return false;
                            return parameters[0].ParameterType == typeof (string) &&
                                   parameters[1].ParameterType == typeof (T).MakeByRefType();
                        });

    var @params = new object[] {value, default(T)};
    return (bool) Parsers[typeof (T)].Invoke(null, @params) ?
        (T) @params[1] : defaultValue;
}

Использование:

var hundredTwentyThree = "123".Parse(0);
var badnumber = "test".Parse(-1);
var date = "01/01/01".Parse<DateTime>();

Даже если вы кешируете MethodInfo в словаре, вызов MethodInfo.Invoke по-прежнему очень медленный ... вместо этого вы должны создавать и кешировать динамически созданные делегаты

Thomas Levesque 21.08.2011 06:08

Если у вас персидский язык и вы должны показывать числа пользователям на персидском языке:

static public string ToFaString         (this string value)
        {
            // 1728 , 1584
            string result = "";
            if (value != null)
            {
                char[] resChar = value.ToCharArray();
                for (int i = 0; i < resChar.Length; i++)
                {
                    if (resChar[i] >= '0' && resChar[i] <= '9')
                        result += (char)(resChar[i] + 1728);
                    else
                        result += resChar[i];
                }
            }
            return result;
        }

Он не обязательно должен быть специфическим для персидского языка, вы можете заставить его работать для любой культуры (например, здесь). Кроме того, вы должны использовать StringBuilder вместо конкатенации строк для повышения производительности.

Thomas Levesque 21.08.2011 05:56

Если вам нужно проверить, что ваша строка Is All char равна 0:

 static public bool   IsAllZero            (this string input)
        {
            if (string.IsNullOrEmpty(input))
                return true;
            foreach (char ch in input)
            {
                if (ch != '0')
                    return false;
            }
            return true;
        }

Это немного излишне иметь что-то настолько специфичное, доступное для каждого строкового объекта в вашем приложении. Особенно, когда вы можете просто использовать myString.Any (c => c! = '0')

MikeKulls 19.08.2011 10:07

@MikeKulls, на самом деле это должен быть myString.All(c => c == '0'), если вы хотите сохранить то же значение ...

Thomas Levesque 21.08.2011 05:28

Иногда нужно иметь экземпляр класса независимо от того, действителен ли он, но не равен null

public static T Safe<T>(this T obj) where T : new()
{
    if (obj == null)
    {
        obj = new T();
    }

    return obj;
}

использование будет таким:

MyClass myClass = Provider.GetSomeResult();
string temp = myClass.Safe().SomeValue;

вместо:

MyClass myClass = Provider.GetSomeResult();
string temp = "some default value";
if (myClass != null)
{
        temp = myClass.SomeValue;
}

извините, если это двуличие, но я его не нахожу.

при сериализации и конфигах лучше использовать long как DateTime, поэтому:

    public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0);

    public static long ToUnixTimestamp(this DateTime dateTime)
    {
        return (long) (dateTime - Epoch).TotalSeconds;
    }

    public static long ToUnixUltraTimestamp(this DateTime dateTime)
    {
        return (long) (dateTime - Epoch).TotalMilliseconds;
    }

и обратно

    public static DateTime ToDateTime(this long unixDateTime)
    {
        return Epoch.AddSeconds(unixDateTime);
    }

    public static DateTime ToDateTimeUltra(this long unixUltraDateTime)
    {
        return Epoch.AddMilliseconds(unixUltraDateTime);
    }

В .NET есть методы IndexOf и LastIndexOf, которые возвращают индекс первого и последнего совпадения в объекте String. У меня есть метод расширения, чтобы получить индекс n-го вхождения:

public static partial class StringExtensions {

    public static int NthIndexOf(this String str, String match, int occurrence) {
        int i = 1;
        int index = 0;

        while (i <= occurrence && 
            ( index = str.IndexOf(match, index + 1) ) != -1) {

            if (i == occurrence) {
                // Occurrence match found!
                return index;
            }
            i++;
        }

        // Match not found
        return -1;
    }
}

Я еще не видел ответа на этот вопрос ...

public static string[] Split(this string value, string regexPattern)
{
    return value.Split(regexPattern, RegexOptions.None);
}

public static string[] Split(this string value, string regexPattern, 
    RegexOptions options)
{
    return Regex.Split(value, regexPattern, options);
}

Использование:

var obj = "test1,test2,test3";
string[] arrays = obj.Split(",");

Используется в Winforms для заполнения comboBox:

List<MyObject> myObjects = new List<MyObject>() { 
    new MyObject() {Name = "a", Id = 0}, 
    new MyObject() {Name = "b", Id = 1}, 
    new MyObject() {Name = "c", Id = 2} }
comboBox.FillDataSource<MyObject>(myObjects, x => x.Name);

Метод расширения:

/** <summary>Fills the System.Windows.Forms.ComboBox object DataSource with a 
 * list of T objects.</summary>
 * <param name = "values">The list of T objects.</param>
 * <param name = "displayedValue">A function to apply to each element to get the 
 * display value.</param>
 */
public static void FillDataSource<T>(this ComboBox comboBox, List<T> values,
    Func<T, String> displayedValue) {

    // Create dataTable
    DataTable data = new DataTable();
    data.Columns.Add("ValueMember", typeof(T));
    data.Columns.Add("DisplayMember");

    for (int i = 0; i < values.Count; i++) {
        // For each value/displayed value

        // Create new row with value & displayed value
        DataRow dr = data.NewRow();
        dr["ValueMember"] = values[i];
        dr["DisplayMember"] = displayedValue(values[i]) ?? "";
        // Add row to the dataTable
        data.Rows.Add(dr);
    }

    // Bind datasource to the comboBox
    comboBox.DataSource = data;
    comboBox.ValueMember = "ValueMember";
    comboBox.DisplayMember = "DisplayMember";
}

Я использую следующие расширения для расширения всех коллекций (может быть, кому-то они пригодятся):

/// <summary>
/// Collection Helper
/// </summary>
/// <remarks>
/// Use IEnumerable by default, but when altering or getting item at index use IList.
/// </remarks>
public static class CollectionHelper
{

    #region Alter;

    /// <summary>
    /// Swap item to another place
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "IndexA">Index a</param>
    /// <param name = "IndexB">Index b</param>
    /// <returns>New collection</returns>
    public static IList<T> Swap<T>(this IList<T> @this, Int32 IndexA, Int32 IndexB)
    {
        T Temp = @this[IndexA];
        @this[IndexA] = @this[IndexB];
        @this[IndexB] = Temp;
        return @this;
    }

    /// <summary>
    /// Swap item to the left
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "Index">Index</param>
    /// <returns>New collection</returns>
    public static IList<T> SwapLeft<T>(this IList<T> @this, Int32 Index)
    {
        return @this.Swap(Index, Index - 1);
    }

    /// <summary>
    /// Swap item to the right
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "Index">Index</param>
    /// <returns>New collection</returns>
    public static IList<T> SwapRight<T>(this IList<T> @this, Int32 Index)
    {
        return @this.Swap(Index, Index + 1);
    }

    #endregion Alter;

    #region Action;

    /// <summary>
    /// Execute action at specified index
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "Index">Index</param>
    /// <param name = "ActionAt">Action to execute</param>
    /// <returns>New collection</returns>
    public static IList<T> ActionAt<T>(this IList<T> @this, Int32 Index, Action<T> ActionAt)
    {
        ActionAt(@this[Index]);
        return @this;
    }

    #endregion Action;

    #region Randomize;

    /// <summary>
    /// Take random items
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "Count">Number of items to take</param>
    /// <returns>New collection</returns>
    public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> @this, Int32 Count)
    {
        return @this.Shuffle().Take(Count);
    }

    /// <summary>
    /// Take random item
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <returns>Item</returns>
    public static T TakeRandom<T>(this IEnumerable<T> @this)
    {
        return @this.TakeRandom(1).Single();
    }

    /// <summary>
    /// Shuffle list
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <returns>New collection</returns>
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> @this)
    {
        return @this.OrderBy(Item => Guid.NewGuid());
    }

    #endregion Randomize;

    #region Navigate;

    /// <summary>
    /// Get next item in collection and give first item, when last item is selected;
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "Index">Index in collection</param>
    /// <returns>Next item</returns>
    public static T Next<T>(this IList<T> @this, ref Int32 Index)
    {
        Index = ++Index >= 0 && Index < @this.Count ? Index : 0;
        return @this[Index];
    }

    /// <summary>
    /// Get previous item in collection and give last item, when first item is selected;
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <param name = "Index">Index in collection</param>
    /// <returns>Previous item</returns>
    public static T Previous<T>(this IList<T> @this, ref Int32 Index)
    {
        Index = --Index >= 0 && Index < @this.Count ? Index : @this.Count - 1;
        return @this[Index];
    }

    #endregion Navigate;

    #region Clone;

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name = "T">Collection type</typeparam>
    /// <param name = "this">Collection</param>
    /// <returns>Cloned collection</returns>
    public static IEnumerable<T> Clone<T>(this IEnumerable<T> @this) where T : ICloneable
    {
        return @this.Select(Item => (T)Item.Clone());
    }

    #endregion Clone;

    #region String;

    /// <summary>
    /// Joins multiple string with Separator
    /// </summary>
    /// <param name = "this">Collection</param>
    /// <param name = "Separator">Separator</param>
    /// <returns>Joined string</returns>
    public static String Join(this IEnumerable<String> @this, String Separator = "")
    {
        return String.Join(Separator, @this);
    }

    #endregion String;

}

Как насчет ...

public static bool IsWinXPOrHigher(this OperatingSystem OS)
{
  return (OS.Platform == PlatformID.Win32NT)
    && ((OS.Version.Major > 5) || ((OS.Version.Major == 5) && (OS.Version.Minor >= 1)));
}

public static bool IsWinVistaOrHigher(this OperatingSystem OS)
{
  return (OS.Platform == PlatformID.Win32NT)
    && (OS.Version.Major >= 6);
}

public static bool IsWin7OrHigher(this OperatingSystem OS)
{
  return (OS.Platform == PlatformID.Win32NT)
    && ((OS.Version.Major > 6) || ((OS.Version.Major == 6) && (OS.Version.Minor >= 1)));
}

public static bool IsWin8OrHigher(this OperatingSystem OS)
{
  return (OS.Platform == PlatformID.Win32NT)
    && ((OS.Version.Major > 6) || ((OS.Version.Major == 6) && (OS.Version.Minor >= 2)));
}

Использование:

if (Environment.OSVersion.IsWinXPOrHigher())
{
  // do stuff
}

if (Environment.OSVersion.IsWinVistaOrHigher())
{
  // do stuff
}

if (Environment.OSVersion.IsWin7OrHigher())
{
  // do stuff
}

if (Environment.OSVersion.IsWin8OrHigher())
{
  // do stuff
}

Еще один, на этот раз, чтобы сделать UriBuilder более дружелюбным при работе с параметрами запроса.

    /// <summary>
    /// Adds the specified query parameter to the URI builder.
    /// </summary>
    /// <param name = "builder">The builder.</param>
    /// <param name = "parameterName">Name of the parameter.</param>
    /// <param name = "value">The URI escaped value.</param>
    /// <returns>The final full query string.</returns>
    public static string AddQueryParam(this UriBuilder builder, string parameterName, string value)
    {
        if (parameterName == null)
            throw new ArgumentNullException("parameterName");

        if (parameterName.Length == 0)
            throw new ArgumentException("The parameter name is empty.");

        if (value == null)
            throw new ArgumentNullException("value");

        if (value.Length == 0)
            throw new ArgumentException("The value is empty.");

        if (builder.Query.Length == 0)
        {
            builder.Query = String.Concat(parameterName, " = ", value);
        }
        else if
            (builder.Query.Contains(String.Concat("&", parameterName, " = "))
            || builder.Query.Contains(String.Concat("?", parameterName, " = ")))
        {
            throw new InvalidOperationException(String.Format("The parameter {0} already exists.", parameterName));
        }
        else
        {
            builder.Query = String.Concat(builder.Query.Substring(1), "&", parameterName, " = ", value);
        }

        return builder.Query;
    }

Другие вопросы по теме