Составим список ответов, где вы разместите свой отличный и любимый методы расширения.
Требование состоит в том, что должен быть опубликован полный код, а также пример и объяснение того, как его использовать.
Основываясь на большом интересе к этой теме, я установил проект с открытым исходным кодом под названием 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. Пожалуйста, присоединяйтесь в любом случае.
Выглядит неплохо. У меня есть комментарий по поводу именования статических классов. Называть их <type> Extensions не очень информативно. Например, StringExtensions содержит как форматирование, так и xml-файлы. Я думаю, что лучше назвать класс, почему вы расширяете этот тип. Например, UnixDateTimeConversions. Вы можете разумно предположить, что он содержит методы для преобразования времени Unix и обратно. Просто мысль!
Проверьте этот URL-адрес, чтобы узнать больше о методах расширения C# planetofcoders.com/c-extension-methods





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».
Да уж! это тоже метод расширения kewl :)
Если вы используете конструктор ArgumentNullException только с 1 строковым аргументом, этот аргумент должен быть просто именем параметра, а не сообщением об ошибке. Итак, ваш код должен выглядеть так: if (obj == null) throw new ArgumentNullException (parameterName);
Я бы использовал для этого default(T) и удалил требование класса.
@Joel: Значения, отличные от значений по умолчанию для собственных типов, чаще являются допустимыми аргументами, чем значения NULL. Для меня проверка на null имеет больше смысла, чем проверка на значение по умолчанию. Конечно, я просто обобщаю всю идею, говоря Require.ThatArgument(input != null) или Require.ThatArgument(personId > 0). Для этого не нужно так много кода, он намного более гибкий и хорошо читается. У меня есть дополнительные переопределения, которые используют функции, когда вы хотите настроить сообщение об ошибке или само исключение.
Чтобы включить имена и типы значений, допускающие значение 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 ();}
строка.Формат ярлыка:
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");
..
Это, конечно, коротко, но будет нечитаемо для новых членов вашей команды.
удобочитаемость - единственная причина, по которой я бы не стал ее использовать, кроме того, это хорошая идея
Я бы назвал его FormatWith или что-то в этом роде.
Я думаю, что удобочитаемость имеет большее значение в более крупной схеме вашего кода, чем несколько сокращенных операторов, которые можно быстро найти / спросить.
Лично мне нужен отдельный объект Formatter, который BCL мог бы анализировать один раз и повторно использовать. Это повысит читаемость и производительность. Я попросил команду BCL - посмотрим ...
Разве это не было сделано? Поскольку это просто еще один способ вызвать в BCL?
chakrit: Нет - потому что каждый раз, когда вы форматируете, BCL будет анализировать строку формата. Строки формата обычно остаются неизменными на протяжении всего жизненного цикла программы - зачем их анализировать более одного раза каждую?
Что это дает, кроме добавления еще одного вызова метода ... Для меня это вообще не выглядит читабельным.
@Jon_Skeet Понятно. Это добавило бы больше ценности методу, хе-хе.
Я не понимаю ... Если вы раньше использовали string.format, то наверняка быстро догадались бы, что это вызов string.format. И быстрое определение goto скажет вам об этом. Если вам это не нравится, просто назовите его «Формат» или как хотите. Взгляните на оператор формата "%" в Python.
Вам следует добавить несколько перегрузок, которые принимают фиксированное количество параметров. Использование параметров происходит медленно!
@Rune Я сделал, я просто привел это в качестве примера. У меня тоже есть собственная версия библиотеки расширений утилит.
Вы не можете называть это «форматом», иначе он не будет компилироваться. Я называю его "Inject", когда использую подобное расширение.
Я бы добавил немного CultureInfo.CurrentCulture и создал другую версию CultureInfo.InvariantCulture.
Переименуйте "F" в "FormatWith" и будет лучше. Функционал хороший, но название нужно поправить.
Console.WriteLine уже имеет перегрузку, которая форматирует. Console.WriteLine ("{0} {1}", значение1, значение2)
Мне нравится название "FormatBy" ... оно немного короче, чем "FormatWith"
Это метод расширения курс, он будет нечитаемым для новых членов команды. Я думал, что это была идея с этой остроумной штукой? Как еще новые участники узнают, насколько мы умны?
Хорошо ... Я просто пошел, чтобы привести это в действие, и выбрал .With - так что вы получите "This is a {0}". With ("test"), и это очень удобно для чтения и имеет смысл. К вашему сведению
Я присоединился к новой команде около 5 месяцев назад и вижу, что этот глупый метод ".F" используется очень часто. И, конечно же, они отказываются менять название ...
gitorious.org/cadenza - это полная библиотека некоторых из наиболее полезных методов расширения, которые я видел.
12 довольно простых методов расширения. Меня немного не впечатляют моно-камни.
(Я говорю о выпущенной версии, а не о той, которую вам нужно получить с помощью системы управления версиями)
Преобразуйте 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
Вы должны использовать десятичное значение для валюты, иначе у вас возникнут проблемы с округлением
Как насчет использования Enum в параметре вместо простой строки
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.
Этот сценарий анализа перечисления возникает постоянно ... нужно поместить это в мою библиотеку :-)
Вау, я писал методы для сопоставления строк с перечислениями (только начал использовать .NET). Спасибо, это абсолютно поможет!
Вы также можете назвать это ToEnum <> (), поскольку оно идет после объекта.
Обратите внимание, что Enum.TryParse <T> был добавлен в Net 4.0 - blogs.msdn.com/bclteam
Я не думаю, что этот метод должен использовать Trim. Обрезка ввода должна быть ответственностью вызывающей стороны.
Обязательно поместите это в проект 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.
}
}
Я обнаружил, что пишу что-то очень похожее.
А что с сумасшедшим действием подстроки? Если вы пытаетесь избавиться от спецификации, есть гораздо лучшие способы сделать это (например, указать, что она не включена в кодировку).
Я бы хотел назвать первый ToXml() (например, ToString())
Ооо, у вас там будет неприятная утечка памяти. Убедитесь, что у вас есть личный Dictionary <Type, XmlSerializer> и кэшируйте их там. Причина этого в том, что каждый раз, когда вы создаете сериализатор .Net загружает новую сборку в ваш домен приложения.
Приносим извинения OP, если он намеренно написал это таким образом, но использование MemoryStreams И XmlReader / XmlWriter было излишним. Классы StringReader и StringWriter идеально подходят для этой операции.
Не проблема !, я сам воспользуюсь этой измененной версией, спасибо.
Просто подумайте ... стоит ли использовать ключевое слово as и просто вернуть результат вместо преобразования и перехвата исключения?
@Jonathan C Dickinson Спасибо за предупреждение!
Остерегайтесь, это небезопасно. Вам обязательно нужно синхронизировать доступ к словарю статических сериализаторов.
@Yann Schwartz С чего бы мне начать?
@Yann, @T, намного проще, если просто добавить атрибут "thread static". Затем для каждого потока будет создан новый кеш. Нет необходимости в синхронизации.
@Jonathan C Dickinson: из документов MSDN здесь msdn.microsoft.com/en-us/library/… видно, что используемый конструктор (новый XmlSerializer (тип)) не имеет проблемы с утечкой памяти. Так, может быть, код кеширования не нужен?
@Jonathan, @Yann, @Frank @slolife, @ TWith2Sugars - После прочтения ссылки MSDN (msdn.microsoft.com/en-us/library/…), предоставленной @slolife, я думаю, можно с уверенностью сказать, что нам не нужно беспокоиться о создании новой сборки при каждом вызове (как было предложено @Jonathan). Я удалил лишнее кеширование и почистил код.
Примеры:
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", поскольку он фактически не устанавливает его в существующем значении. Хотя в остальном неплохо.
DateTime.Now.First () - что в первую очередь? Это видно только из примера кода.
Очень хорошо. Но согласитесь, названия могли бы быть намного лучше.
DateTime.Now.First будет достаточно ясным в Intellisense, если метод хорошо документирован.
Легко сериализуйте объекты в 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/…
Еще один полезный для меня:
/// <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 и свести его к одному возврату, если хотите.
ага, у меня тоже была куча таких. Но он был слишком большим, когда вам нужно было string.ToInt32 double.ToInt32 float.ToInt32 .. бла-бла Я думаю, что я немного злоупотребил этим :-)
Пабло Марамбио - какие альтернативные способы я мог бы выполнить?
Если я правильно помню, convert.ToInt32 мог вызвать исключение
У меня есть аналогичный метод Parse <T>, который я использую для всех типов, а не только для int32.
У меня есть различные методы расширения в моем проекте 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
Да, я когда-нибудь перенесу его в Google Code ... и, наверное, заодно переименую! (MiscUtil - ужасное имя.)
@bovium: Вы уже видите код. Перейдите по ссылке в первом предложении - там есть полный исходный код.
@ Джон Скит. Могу ли я использовать код и поместить его в проект Codeplex?
@bovium: Я лучше сделаю это сам, выложу на code.google.com и сам буду управлять проектом, если вы не возражаете. Очевидно, что вы имеете право разместить его на Codeplex, если сохраните соответствующую атрибуцию, но я лучше разберусь с этим сам в ближайшее время, если вы не в отчаянии :)
@ Джон Скит. Он размещен под лицензией MIT, и его можно использовать бесплатно для всех. Коммерчески или с открытым исходным кодом. Почему бы не объединить усилия и не создать публичную библиотеку методов расширения.
Просто потому, что я делаю много других кусочков в этой библиотеке. Вы можете сделать копию всего этого для своего проекта, но я бы предпочел сохранить одну копию в моем собственном проекте.
x.ThrowIfNull мне кажется неправильным. Вы вызываете метод того, что выглядит как член экземпляра, проверяя его значение null. Я знаю, что это сработает, поскольку методы расширения - это просто статические методы, это просто не интуитивно понятно.
@Josh: Сначала это неинтуитивно, но как только вы к этому привыкнете (что В самом деле не займет много времени), он будет кратким и очень читаемым. Держите свой разум открытым для новых способов делать что-то :)
Джон, на вашем сайте сказано, что это "под лицензией Apache ...". Какая лицензия правильная: MIT / Apache? Спасибо
19.June(1976) в порядке (по крайней мере, для тестирования), но мне не нравится 30.Minutes(). Почему бы не использовать перегрузку операторов, чтобы достичь чего-то более эквивалентного научным обозначениям, например 30 * min или 30 * Minutes, если хотите.
@lasseespeholt: Ну, в обоих случаях вам нужно, чтобы min и Minutes были определены где-то в каждом исходном файле, который их использует ... и это может раздражать. В случае метода расширения это всего лишь случай добавления директивы using (на которую мы все равно не обращаем внимания). Но это определенно дело вкуса.
@Jon Ahh хороший момент;) потому что он может быть статическим, поэтому вы можете сделать что-то вроде 30 * SI.min, но это просто сделает его менее понятным.
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 и т. д.
Мне это нравится, но я пытаюсь решить, правильно ли делать проверку границ включительно для минимального значения, но исключая для максимального значения. Интересно, сбивает ли это с толку. 5.Between (5,10) - истина, а 5.Between (1,5) - ложь. Даже не уверен, что метод-компаньон Within поможет. Thougts?
Разве название «IsBetween» не имело бы больше смысла? Также можно сделать IsBetweenInclusive и IsBetweenExclusive. Не знаю, какой из них выбрать по умолчанию.
@Steve: было бы больше смысла, если бы это было расширение datetime.
Для меня между подразумевается: 5.Between (5,10) возвращает false, а 10.Between (5,10) также возвращает false. Мне это кажется естественным.
Мне кажется, что у разных людей разные представления о том, что естественно. Из-за этого, вероятно, следует явно указать, что используется (например, «Включающий» против «Исключающий»), поскольку это может быть очень простым источником ошибок.
И Python, и Common Lisp используют инклюзивный на нижнем уровне и эксклюзивный на верхнем, как в приведенном выше коде. Да, сначала это кажется «неестественным», но в первые 5 минут он становится чрезвычайно полезным - я бы классифицировал его как неправильный. В конце концов, каждый символ в приведенном выше коде (кроме, возможно, <) сначала тоже казался мне «неестественным» - писать public static class ComparableExtensions { до того, как мой код, не совсем интуитивно.
Я сделал аналогичный метод, но я также добавил перегрузку, которая использует перечисление BetweenType, чтобы указать, как должно происходить сравнение. иногда вам нужно истинное между (исключительное), иногда включающее, а иногда включающее ту или иную границу.
Delphi избегает путаницы с «между», вызывая свою функцию «InRange» и возвращая истину в конечных точках. Я считаю, что это более полезное имя, чем двусмысленное «между».
Я бы, вероятно, просто добавил перегрузку с третьим параметром, чтобы сделать его более читаемым, и предоставить сопутствующее перечисление Match.Inclusive, Match.Exclusive. По умолчанию третий параметр делает его включающим, но перегрузка позволяет явно указать это: 5.Between (1, 5); возвращает true, но 5.Between (1, 5, Match.Exclusive); возвращает false.
Будьте осторожны при использовании поплавка или двойника. Их семантика различается между IComparer и операторами <=> для бесконечностей и NaN. То, что 0 подписан (есть разные +0 и -0), тоже может создать проблемы.
По умолчанию должно быть включено. Если кто-то говорит вам: «Выберите число от 1 до 9», в большинстве ситуаций возможными ответами будут 1 и 9.
Используйте enum (neither, lower, upper, both) в качестве третьего параметра, чтобы указать равенство границ. по умолчанию neither.
Более простой способ загрузить настройки по умолчанию из коллекции (в реальной жизни я использую его для заполнения настроек из любого источника, включая командную строку, параметры 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" } });
Хм, а что именно с этим было не так? Я не возражаю против отрицательного голоса, но я, честно говоря, хотел бы знать, должно ли оно работать или что-то в этом роде.
Полезно для модульного тестирования:
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?
да. (Обязательное ограничение на количество символов комментария здесь.)
Мне нравятся эти расширения NUnit Assert: http://svn.caffeine-it.com/openrasta/trunk/src/Rasta.Testing/AssertExtensions.cs
Выскакивает с паролем. Просто нажмите "Отмена".
Метод расширения:
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>
Просто используйте инициализатор коллекции => var list = new List<int>{5,4,8,4,2};
Почему бы просто не вызвать List <T> .AddRange (IEnumerable <T> collection) в вашем методе?
@Will: Фактически, было бы Лучший, чтобы принять ICollection<T>; тогда его можно было бы использовать, например, на LinkedList<T> и HashSet<T>, а не только на индексированных коллекциях.
@Arnis L это позволяет вам ДОБАВЛЯТЬ элементы в уже существующий List <T> и может вызываться несколько раз.
Я бы переименовал это в AppendRange() - это делает разницу между этим и инициализатором коллекции более очевидной.
Почему вы зацикливаете значения, когда можете вызвать list.AddRange(values);?
Отредактировано как разрешить ковариацию в pre-.net 4.0
Не компилируется, проблема с S и T.
Мне не нужно добавлять кучу жестко закодированных значений в список почти так часто, как мне обычно нужно добавлять значения в IEnumerable. Когда мне нужно сделать что-то подобное, я просто говорю items = items.Concat(new[] {5, 4, 8, 4, 2}). Он работает с любым IEnumerable (как для субъекта, так и для аргумента), и если вы действительно хотите получить список, просто вызовите ToList(), когда закончите.
Обобщено на ICollection <T>
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 ();
О, чувак - офигенный соус! я никогда этого не знал! ура кучи чувак. (мой пост выше, отредактированный.)
Рад, что вам это нравится. Одно - Any () возвращает логическое значение, поэтому <= 0, вероятно, не будет компилироваться. Если перечисляемое пусто, Any () вернет false. Этот шрифт затрудняет просмотр, но в моем исходном примере перед вызовом Any был восклицательный знак.
@ Pure.Krome Могу ли я использовать код в проекте codeplex. И вы не хотите стать участником этого проекта?
ToPluralString () просто упрощен. Английский не является моим родным языком, поэтому мне это кажется глупым, но он даже не очень хорошо работает с английским языком в целом. ;-)
@peSHIr: хотите привести пример? в основном, для большого количества (давайте будем упрощать и сказать «большинство») «вещей», которые либо 0, либо 2+ .. оканчиваются s, чтобы определить, что они являются множественным числом (или нет для 0). например. кошка / кошки. собака / собаки. друг / друзья. ошибка / ошибки. Теперь это не сработает для таких вещей, как фея / феи (и все другие слова, оканчивающиеся на y) или зуб / зубы ... и т.д. модификации единственного числа во множественное число.
@ Pure.Krome: Вы уже сами приводите примеры. ;-) Еще: index / index, saldo / saldi и другие латинские. Если это просто помощник для использования в ToReadableTime () - где он будет отлично работать, даже если он никак не глобализован, но функция не используется (?) - я бы ожидал, что ToPluralString () будет закрытым вместо этого общественности.
Это какая польза?
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). красивый.
Пара вещей: я бы рекомендовал, чтобы OneOf принимал ЛюбыеIList<T>. Тогда вы всегда можете иметь перегрузку также, которая принимает аргумент params и просто передает его в перегрузку IList<T>. Я дал ответ (прямо сейчас внизу) с помощью метода NextBool, аналогичного вашему CoinToss, но с перегрузкой, которая принимает параметр probability (что, если я хочу, чтобы что-то происходило в 75% случаев?). Кроме того, просто придирка: ваш пример кода выдаст NullReferenceException, поскольку rand никогда не инициализируется.
+1 Мне это очень нравится, но я предпочитаю, чтобы CoinToss был реализован с rng.NextDouble() < .5, потому что внутри .Next(int) сделан с .NextDouble(), поэтому вы должны сохранить приведение, * и чек.
Вот тот, который я часто использую для форматирования презентаций.
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 и т. д. Пожалуйста, поймите что-нибудь конкретное.
Больше примеров можно найти здесь: 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.
Собственно, сделайте параметр this типа IDataRecord для максимальной совместимости. В моей версии у меня есть перегрузка, которая принимает порядковый номер, который вызывает версия fieldName. Сохраняет «GetOrdinal» с последующим поиском по имени.
Существует подходящая реализация, которая может работать с любым типом значения: rabdullin.com/journal/2008/12/6/…
Спасибо, Ринат, я фактически свел это к одному универсальному методу - см. stackoverflow.com/questions/303287
Все эти методы кажутся ненужными, поскольку вы можете использовать ключевое слово as, чтобы получить значение от считывателя, допускающее значение null. Если вы комбинируете нулевой объединяющий оператор ?? с оператором as, вы можете даже иметь ненулевое значение по умолчанию для прямого перехода к типу значения. См. stackoverflow.com/questions/746767/…
@Stevo Нет, вы не можете использовать as таким образом. Вы это проверяли? Значения DBNull вызовут исключение.
@Adam Lassek - На самом деле да, я тестировал это и запускал в производственной среде. Я понятия не имею, откуда у вас создалось впечатление, что будет сгенерировано исключение, если значение равно DBNull, это действительно не так; то есть доступ к данным 101.
@Steve, я думаю, что это зависит от провайдера .. У меня были провайдеры оракулов, которые взорвали баллы DBNull, но некоторые другие этого не сделали. Хотя слишком долго, я, наверное, ошибаюсь;)
Я рекомендую посмотреть, можно ли это сделать для строго типизированных таблиц данных? они также не допускают типов, допускающих значение NULL, и с ними очень сложно работать. На самом деле они намного хуже, потому что, если вы попытаетесь получить доступ к полю с нулевым значением, сам вызовет исключение ... он даже не вернет dbnull, который вы можете проверить.
Я использую их в своих веб-проектах, в основном с 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 --%>
<% } %>
Кто угодно может его использовать - угощайтесь сами
Очень хорошо, может быть расширен, чтобы сначала проверить строку запроса, затем проверить ViewData, затем проверить SessionState и, наконец, вернуть значение по умолчанию.
Мои расширения преобразования, которые позволяют:
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 следует использовать для ошибок, которые возникают вне предполагаемой логики, ИМО. хммммм
Если бы я не хотел, чтобы вы использовали код, я бы его не опубликовал! :)
Наконец-то что-то невидимое. Мне это нравится. :)
Вы должны, по крайней мере, изменить это предложение «catch», чтобы перехватить только те исключения, которые вызовет ChangeType (), когда он не может «преобразовать» ссылку. Я думаю, вы не хотели бы, чтобы какие-либо OutOfMemoryException, ExecutionEngineException, ThreadAbortException или тому подобное рассматривались как ошибка преобразования. В противном случае было бы довольно сложно отслеживать ошибки.
Я считаю, что ToOrNull ведет себя точно так же, как ToOrDefault (т.е. если вы вызываете ToOrDefault для ссылочного типа с неудачным преобразованием, он вернет null). Но что более важно, мне это кажется излишним, поскольку var s = myObject as string выполняет то же самое, что и var s = myObject.ToOrNull<string>(), но без потенциальной необходимости ловить InvalidCastException. Я что-то упускаю?
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 я думаю ... но я недостаточно хорошо знаю Руби, чтобы быть уверенным.
+1. Я не знаю, почему .NET BCL не имеет расширения .ForEach для IEnumerable <T>
Я разместил это где-то еще на SO, но кто-то сбил. Как-то связано с тем, что он завершает выражение (т.е. не возвращает IEnumerable <T>).
Сообщение о том, почему расширения LINQ IEnumerable <T> не включают ForEach: stackoverflow.com/questions/317874/…
Я рекомендую прочитать это перед использованием метода: blogs.msdn.com/ericlippert/archive/2009/05/18/…
Реактивные расширения включают IEnumerable <T> .Run () со многими перегрузками. Очевидно, группа Эрика Мейера не в ладах с группой Эрика Липперта.
@jpbochi: Это просто демагогия Microsoft
@abatishchev И ваш комментарий - это просто предубеждение против Microsoft. Это не отменяет ни одного слова, написанного Эриком. Чьи-то аргументы не считаются действительными или недействительными только из-за компании, в которой он / она работает.
Между прочим, позвольте мне прояснить один момент. Я не говорил, что вам не следует использовать этот метод расширения ForEach. Я просто сказал, что вам следует рассмотреть моменты, которые раскрыл Эрик, прежде чем вы решите, использовать это или нет. Я прочитал и решил не использовать. Вы можете делать со своим кодом все, что хотите.
@jpbochi: Я очень уважаю Эрика. Тем не менее он зачитывает официальную архитектурную позицию команды Microsoft, которую я назвал демагогией.
ReactiveExtensions предназначены для «реактивного» программирования. Естественно, если вы разбираетесь в «реактивном» программировании, вам потребуется метод «Run ()». Между группами нет никаких разногласий. Они просто предлагают два разных способа кодирования с использованием двух разных фреймворков.
Функция для сравнения файлов / каталогов через Информация о файловой системе ОС. Это полезно для сравнения общих ресурсов с локальными файлами.
Использование:
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));
}
}
При этом не используются методы расширения. Это просто статический класс.
Чтобы изменить метод расширения: static public bool CompareFiles (string path1, string path2) на static public bool IsSameFileAs (this string path1, string path2); затем используйте как: if (file1.IsSameFileAs (file2)
Два разных файла на разных дисках могут по совпадению иметь один и тот же FileIndex. Вам также нужно сравнить VolumeSerialNumber - но тогда ваш пример не удастся, поскольку VolumeSerialNumbers разные.
См. Также stackoverflow.com/questions/410705/…
Разве вы не должны закрыть эти дескрипторы файлов?
Я не хотел добавлять ничего из того, что уже было сказано, поэтому вот некоторые из них, которые я использую, но не упомянутые. (Извините, если это слишком долго):
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)?
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 для разработки по контракту.
Разве не опасно уже перечислять "enumerable", вызывая "Count" при проверке контракта? Или это не проверка во время выполнения?
Это по-прежнему опасно, потому что некоторые перечисления можно повторять только один раз, но я исправил это так, что, по крайней мере, он останавливается после двух итераций, а не определяет все количество.
Пифонические методы для словарей:
/// <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(...).
Лучше использовать TryGetValue, вы выполняете два поиска вместо одного.
«Пожалуйста, отметьте свои ответы согласием на включение кода в проект 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 ());
Реализация с использованием StringBuilder должна быть быстрее.
@CodeInChaos Тестирование в stackoverflow.com/questions/228038 показало, что StringBuilder работает медленнее.
Ты прав. Похоже, что требования к безопасности потоков (вероятно, для обеспечения неизменности строки, возвращаемой ToString) сильно замедляют StringBuilder.
Надеюсь, вы не встретите суррогатов или комбинирующих персонажей.
Или даже строки новой строки в форме "\r\n". Или двойные символы новой строки в форме "\n\r", если на то пошло.
У меня есть метод расширения для регистрации исключений:
public static void Log(this Exception obj)
{
//your logging logic here
}
И используется он так:
try
{
//Your stuff here
}
catch(Exception ex)
{
ex.Log();
}
[извините за отправку дважды; второй лучше спроектирован :-)]
Может быть, следует читать общедоступный статический журнал void (этот объект Exception) {}?
Я думаю, что это хорошо для BCL или сторонних исключений, но если вы откатываете свои собственные типы исключений, вы можете поместить ведение журнала в свой базовый класс исключений. Таким образом, вам не нужно помнить о вызове Log ().
Вот еще одна реализация 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 < 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, было интерпретировано как тег ... просто изменил его на <
Я использую его довольно часто ...
Исходный код:
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 (); возвратный гид; ... могло бы быть лучше с семантической точки зрения.
Я разочарован тем, что .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. Возможно, вы захотите профилировать их.
Этот для 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
Вероятно, это следует продублировать с помощью Literal.
Метод 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
Некоторые расширения для работы со списками:
/// <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, и не вернуть «настоящие ленивые» счетчики? В какой-то мере позаботится о вашем высокопроизводительном комментарии к коду.
Я бы переименовал их в Append() (и использовал блоки итераторов, как это было предложено peSHlr)
Джоэл, peSHIr: хорошие предложения, я обновил ответ, чтобы сделать это.
Эти методы расширения очень полезны для меня при анализе ввода формы перед помещением в базу данных.
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, могут быть байтовые и длинные
Хорошие моменты, Ринат. Я бы хотел, чтобы был способ написать эти методы расширения.
Если вы хотите сделать это с помощью перечислений, измените тип object на просто Enum.
Расширения, связанные с 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
Для добавления нескольких элементов в коллекцию, в которой нет 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());
Я использую этот метод расширения обычно с анонимными типами, чтобы получить словарь 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
Преобразуйте любую строку в тип 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
Общая попытка:
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 является структурой.
Не знаю, но, может быть, там должно быть место T: class.
Вам по-прежнему нужно указывать имя переменной перед каждым вызовом метода / свойства. Почему бы просто не сократить исходное имя переменной?
@Joel Muller, это не всегда переменная мой ... подумайте Someone.Else.Namespace.With.WeirdClass.Instance
Я также использую синтаксис инициализатора свойств C# 3.0 везде, где это возможно, для достижения того же результата.
@chakrit, вот пример. Применяется только при создании объекта Button n = new Button {Name = "Button1", Width = 100, Height = 20, Enabled = true};
Это было бы полезно, когда у вас есть много событий для подключения, потому что синтаксис инициализатора свойств C# не поддерживает события.
это также полезно вне инициализаторов свойств, потому что вы можете использовать их только при создании нового объекта. это расширение может работать с ранее созданными объектами.
Итак, что плохого в простой записи { var x = someVeryVeryLonggggVariableName; /* do something with x */ }?
@Heinzi Вы получаете новую область видимости (область видимости блока With) и возможность встроенного комплексного доступа к объекту. Кроме того, это не так сильно прерывает ход ваших мыслей, поскольку вам не нужно возвращаться и помещать его в переменную. См .: msdn.microsoft.com/en-us/library/ms172864(VS.80).aspx ...
Пожалуй, самые полезные методы расширения, которые я написал и использовал, здесь:
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, в этом весь смысл предиката.
Отчасти идея заключается в том, что некоторые условные выражения не будут выполняться в SQL с IQueryable, поэтому вам придется их разделять.
Встроенные преобразования: мне нравится этот небольшой шаблон. Завершено для 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);
{
Мне нравится, но я бы не сказал «Есть» или «Как», потому что это подразумевает сравнение / преобразование типов. Это действительно метод разбора.
Вы можете использовать ParseIsInt32 (), ParseToInt32 и ParseAsInt32 ()
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. :)
Этот код не компилируется. У массива нет метода Contains
Он компилируется, если вы используете System.Linq;
Может быть, «EqualsAnyOf» было бы лучше, чем «In»?
Не уверен, что мне это нравится - мне нравится краткость In, но, возможно, IsIn был бы лучше.
Используя тот же метод Contains: (new [] {1, 2, 3}). Contains (a)
Ошибка Visual Studio: ошибка 2 'System.Array' не содержит определения для 'Contains', и не может быть найден метод расширения 'Contains', принимающий первый аргумент типа 'System.Array' (отсутствует ли у вас директива using или ссылка на сборку?)
Проблема: для этого требуется более 1 параметра. В таком случае лучше всего использовать метод LINQ contains, верно?
Я бы, наверное, написал два метода расширения - IsInOne () и IsInAll ()
@webDevHobo - System.Linq является обязательным оператором using, также это означает, что у вас должен быть linqBridge.dll или .net 3.5 или выше. Содержит - msdn.microsoft.com/en-us/library/bb339118.aspx LinqBridge - code.google.com/p/linqbridge
Я тоже подумал о In<T>(...) и обнаружил, что это самый полезный метод расширения за пределами стандартной библиотеки. Но я не согласен с названием In. Предполагается, что имя метода описывает то, что он делает, но In этого не делает. Я назвал его IsAnyOf<T>(...), но думаю, IsIn<T>(...) тоже подойдет.
Название имеет смысл, особенно если вы использовали аналогичные функции в MySQL. dev.mysql.com/doc/refman/5.5/en/…
Меня раздражало, что 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", этот метод полезен для его исправления. Коллекция никогда не должна быть нулевой.
Несколько раз я обнаруживал, что мне нужно что-то вроде, я думаю, 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);
Думаю, было бы неплохо, даже если бы это тоже могло доставить неприятности.
Если вы думаете, что это хорошая идея:
Может быть, это просто плохая идея: D, но я считаю, что это может быть полезно, если все сделано правильно. Если ничего подобного нет, и вы думаете, что он имеет какую-то ценность, я могу попробовать и потом отредактировать ответ.
требует .NET 3.5, использует делегатов для оценки графа объекта в try catch stackoverflow.com/questions/298009/…
Я думаю, что это может быть хорошей идеей, но ее сложно реализовать ... Вы можете легко проверить, является ли x нулевым, но если вы хотите выполнить проверку для каждого уровня "пути" (SomeProperty, MaybeAMethod), он получит очень трудно. Я попытался сделать это, переписав дерево выражений, но в конце концов сдался ...
Это должен быть отдельный вопрос
В конце концов мне удалось заставить его работать: tomlev2.wordpress.com/2010/02/21/…
Эй, это здорово !. Я действительно попробовал это пару дней назад и придумал это: bit.ly/9777Y0, но ваш подход кажется намного чище. Но я согласен с вашими выводами.
Иногда удобно записать строку для выбранного элемента в списке с помощью специального разделителя.
Например, если у вас есть 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.
Ниже приведен метод расширения, который адаптирует Код Рика Страла (и комментарии тоже), чтобы вам не приходилось угадывать или читать метку порядка байтов массива байтов или текстового файла каждый раз, когда вы конвертируете его в строку.
Этот фрагмент позволяет вам просто делать:
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();
}
}
}
}
Очень полезный метод, но я не думаю, что он должен быть методом расширения.
Если вы пишете текстовый редактор, это, вероятно, требует использования метода расширения, но в большинстве случаев я согласен, что это, вероятно, не более чем статический частный метод.
Я всегда использую формат, который требует новой строки с 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 - это не еще одна лишняя строка (хотя и просто \ n)?
AppendLine использует статическую строку «\ r \ n». string.Format каждый раз создает новую строку, которая должна быть GC: d.
Двое маленьких (некоторые считают их глупыми), которых я вкладываю во все свои проекты:
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); }
Преимущество использования дженериков заключается в следующем: если вы попытаетесь вызвать общую версию IsNull () Дэна Дипло для структуры, вы получите ошибку времени компиляции. Если вы вызываете оригинальную версию Джона Крафта, она не предупреждает вас (а также добавляет бокс-код).
@Ray Burns: Вы абсолютно правы, однако я считаю это плюсом для себя; он не вызовет исключения, и упаковка является предпочтительным поведением в коде, который я пишу. Кроме того, в текущей среде, в которой я работаю (бизнес-приложения CRUD), нам никогда не нужны структуры; так что это не имеет значения.
Это не плюс, потому что он всегда возвращает false для структур. Это означает, что если кто-то использует его в структуре, скорее всего, он сделал что-то по ошибке, но не получит ни одной ошибки или предупреждения. Учитывая, что метод Дэна выглядит идентично, когда вы его вызываете (т. Е. Нет недостатков в его использовании), то вы никак не можете считать свою версию плюсом.
Меня смущает беспокойство по поводу структур. Структура по определению никогда не может быть нулевой. Поэтому тот факт, что он возвращает false для структуры, является правильным поведением.
+1, я использую их все время. Я как раз собирался отправить ответ с тем же кодом точно, пока не обнаружил, что ваш ответ уже существует: D
Я использую их в своих проектах 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 символа, то есть строку длиннее, чем указано в документации.
@Simon - Исправлена документация. @peSHIr - Я редактировал, не могли бы вы уточнить.
Удобный способ разобраться с размерами:
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
};
}
}
На мой взгляд, это действительно плохой стиль кодирования. Вместо этого следует использовать константы, а не запутанную логику.
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 вместо литья
Шаблон для синтаксического анализа, который избегает параметров 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.
Может ли кто-нибудь быть достаточно любезным, чтобы объяснить это менее одаренным из нас?
хахаха. Просто прочтите статью (комментарий Джоэла выше) - забавно, правда, но, будучи почти в той же лодке (в конце приема, а не в конце Паулы), это только забавно, оглядываясь назад! Однажды к работе над проектом, в котором я работал дизайнером / ведущим разработчиком, был привлечен подрядчик - она не находилась под моим непосредственным контролем, но ей была поручена работа из рабочего списка моей команды. Боссы хвалили ее как блестящую (даже позже наняли ее снова в качестве руководителя разработки!). Они никогда не догадывались, что каждый фрагмент кода, который она написала или спроектировал, не попал в производство, и все должны были быть полностью переписаны моей командой с нуля!
Бинарный поиск:
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.
Ой, почему бы и нет! Вот расширение 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
Нет примера? Это довольно просто, но, возможно, не для всех
Было бы больше .NETy, если бы вы использовали IComparer<T> или Comparison<T> (или перегрузку для каждого) вместо функции «меньше», которая попахивает STL.
Насколько я понимаю, Comparison <T> - это в основном замаскированный Func <T, T, int>, где мне нужен bool для работы с сортировкой вставки.
Да, Comparison<T> эквивалентен Func<T, T, int>, который имеет тот же интерфейс, что и IComparer<T>.Compare. Это стандартный интерфейс компаратора, к которому привыкли разработчики .NET. Большинству функций сортировки нужно сравнивать только меньше или больше. Вы выбрали меньше. Если вы посмотрите в Reflector на Array.SorterGenericArray.QuickSort() (или Array.SorterObjectArray.QuickSort()), вы увидите, что Array.Sort также использует только меньше, но делает это с comparer.Compare(a, b) < 0, сохраняя установленный интерфейс для платформы.
Принятие IComparer<T> также позволяет вашим пользователям (или вам) использовать Comparer<T>.Default вместо того, чтобы выполнять сравнение вручную. Лучший интерфейс при сравнении обычно имеет три перегрузки: одна принимает IComparer<T>, другая - Comparison<T>, а третья не использует компаратор и предполагает Comparer<T>.Default.
В разделе недавних поисков на странице статистики моего блога я удалил все дубликаты, но мне нужен был способ удалить почти повторяющиеся строки. Я бы получил массу похожих, но не совсем одинаковых запросов 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.
Некоторые расширения 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, но не так эффективен.
Некоторые удобные помощники по строкам:
Использование:
Я ненавижу нежелательные пробелы в конце или в начале строки, и поскольку строка может принимать значение 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 в виде строки.
Ребята из BCL, но да, привет 5 действительно.
Я реализовал пакет методов расширения (доступных по адресу 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 с одним значением. Может ли кто-нибудь отредактировать ответ, чтобы прояснить этот момент?
«Труба» отличается от «Где». «Где» - это «карта» (в математическом смысле и на функциональных языках), которая принимает набор (коллекция и в случае .NET IEnumerable <T>) и функцию (которая в .NET-мире является делегатом и может быть представлена с помощью лямбда-выражения, такого как x => x> 2; единственное ограничение для предоставленного предиката состоит в том, что он должен возвращать логическое значение. Оператор «конвейер» - распространенный инструмент в функциональных языках. Его основное использование - для объединения вычислений (вызовов функций). Он получает значение и функцию (например, x => f (x)); затем он применяет функцию к значению и возвращает результат.
Боюсь, что некоторые из них мне не нравятся. Например, 500.Sleep () ... На мой взгляд, это слишком загадочно. Я не вижу, что не так с обычным Thread.Sleep ()
Для 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 или для подкладки в клетку для птиц.
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: Это отличный момент. Однако есть элегантный способ использовать отложенное выполнение без явного объявления класса. yield return решает проблему. Отредактирую свой ответ.
Твердый. Теперь это в основном логическая противоположность OrderBy. Отлично сделано!
Только что нашел здесь немного более гибкую версию: stackoverflow.com/a/5807238/123897
Вы должны удалить цикл foreach и заменить тело оператора if на yield return array[k] = array[n];.
Работая с 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;
}
}
}
Я только что просмотрел все 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 в строке и почувствовал себя глупо, когда понял), но он будет хорошо работать для типов значений
Обнуляемые tpyes имеют встроенное свойство HasValue.
@johnc, нет, вызов не завершится неудачно, если o равно нулю. Методы расширения - это фактически статические методы, а не методы экземпляра. И метод расширения IsNullOrEmpty у меня отлично работает ...
Я считаю весьма полезным следующий метод расширения:
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;
}
В словаре есть отдельное свойство коллекции ключей, которое может сделать это быстрее.
Если вам нужны ключи без учета регистра, вы можете передать StringComparer.InvariantIgnoreCase конструктору словаря.
@ Томас - Даже лучше! Предполагается, что у вас есть доступ к ctor, но это определенно лучший подход.
Я использую этот все время:
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.
@ Джим Шуберт: Можете ли вы подтвердить это некоторыми тестами? Размер IL не является хорошим показателем скорости. Меньшее количество инструкций автоматически не означает более быстрое выполнение.
Просто отметьте, что это все еще полезный метод для StringBuilder. Если вы хотите разделить вызовы метода соединения с другим кодом, тогда у вас будет несколько вызовов string.Join в любом случае, что сведет на нет любое преимущество в производительности по сравнению с использованием StringBuilder.
@John: Тесты проводились тысячи раз, и они редко, если вообще когда-либо, показывают какую-либо заметную разницу в производительности. Я хотел бы провести тесты, чтобы увидеть разницу, и сравнить string.Join / Format / Concat, StringBuilder.Append / AppendFormat и конкатенацию операторов +. Однако разницу в 300 мс при построении строки можно легко компенсировать за счет производительности сети и базы данных.
Подобно строке 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 Это субъективное предпочтение, но оно уменьшает количество запутанных парнеров, которые могут накапливаться, когда у вас есть несколько приведений. item as Type становится (item as Type) или ((Type) item), если вам нужно использовать item как тип приведения. Также сканирование слева направо элемента item.As <Type> (). ... в некоторых запутанных случаях гораздо удобнее читать, чем бокс. Я сказал, что это было просто, и я согласен, что это субъективно, но я считаю, что это может быть довольно мощным инструментом для удобочитаемости кода.
@Kamarey некоторые люди называют это «беглым» программированием - всегда программировать слева направо, никогда не нужно делать резервную копию, чтобы поставить скобки на вещи. Достижение клавиш со стрелками замедляет работу. Он также хорошо сочетается с операторами Enumerable и Observable. @johnc Я бы добавил To<T> в список, который делает (T)item.
Пара полезных расширений, если вы работаете с финансовыми годами
/// <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/… (хотя, поскольку это статическое свойство, оно на самом деле не работает как метод расширения).
// 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 IEnumerable <T> .Any () - только общий IEnumerable <T> имеет Any ().
Преобразует список в таблицу данных
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/…
Я уверен, что это уже делалось раньше, но я часто использую этот метод (и более простые производные):
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 параметрами требует рефакторинга ...
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);
}
красивый. То, что я ищу :-)
@ aboveyou00, в основном для производительности. Кроме того, нет очевидной эквивалентности между регулярным выражением и шаблоном подстановки, а подстановочный знак проще использовать, если вам не нужна вся мощь регулярного выражения.
Эти методы расширения вызывают событие асинхронно. Они были вдохновлены этот ответ 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.
Я сам не раз писал практически идентичный код.
Интересно. Но не совсем методы расширения.
Перезаписать часть строки по указанному индексу.
Мне приходится работать с системой, которая ожидает, что некоторые входные значения будут фиксированной шириной и строками с фиксированным положением.
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 не изменяет исходную строку.
Некоторые функции даты:
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 (). В них заключен довольно тривиальный код, но они значительно улучшают читаемость.
Одно из моих любимых - расширение 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;
}
}
По назначению очень похоже на метод, который я опубликовал здесь. Ваша реализация позволяет использовать более гибкие шаблоны, но моя, вероятно, быстрее;)
Вот тот, который я только что создал.
// 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 вам не понадобится, и оно будет работать и для типов значений.
TIn должен быть классом, иначе весь этот метод расширения не имеет смысла (типы значений не могут быть нулевыми). И ваш пример с t.Count действительно работает с указанным выше методом расширения. Не могли бы вы взглянуть еще раз?
@Scott: это полезный метод решения распространенной проблемы. Однако я считаю, что TReturn elseValue = default(TReturn) доступен только для .NET 4.0? У меня 3.5 SP1, и я никогда не видел эту конструкцию (как и мой компилятор). Я просто переместил это внутрь метода. Однако одна проблема заключается в том, что упаковка типа, допускающего значение NULL, в объект для использования с методом дает неожиданный результат (0 против ожидаемого NULL).
@Jim: ключевое слово default(T) существует с VS2005, но я думаю, что параметры по умолчанию - это новая функция .NET 4. Самый простой способ обойти это - иметь два варианта: один, который принимает параметр, а другой - нет. Я обновлю ответ, чтобы он был совместим с CLR 2.0. По поводу бокса - в этом суть default. Это будут 0-инициализированные данные для типа значения и null для всех ссылочных типов. TReturn типа значения должен оставаться распакованным на протяжении всей функции.
@Scott: Мой вопрос касался параметра по умолчанию, который я видел только в динамических языках, таких как Ruby. Моя точка зрения относительно типов, допускающих значение NULL, заключается в том, что возвращаемый x.Value должен возвращать значение NULL (если, например, int? был нулевым) или значение, если int? имеет значение. Возврат 0, когда int? x = null передается и помещается в объект, является странным случаем. Я видел аналогичные проверки типов, допускающих значение NULL, в библиотеках, таких как fluent nhibernate и linfu (я думаю) для этого конкретного случая, что позволяет вам отказаться от ограничения класса, как предлагалось ранее.
@Jim Я считаю, что такой параметр по умолчанию также возможен в C++. Я не знаю, почему это было бы распространено только на динамическом языке, потому что это статически типизированная функция (параметры шаблона известны во время компиляции). Что касается int?, требуется ограничение класса, иначе функция не будет компилироваться. Вы не можете сравнивать с null, не требуя ссылочного типа. Теперь можно иметь вторую версию функции, которая принимает TIn?. Но что она должна делать? Вернуть нуль или Value в штучной упаковке? Но у нас это уже есть в форме обнуляемого ... Я не вижу добавленной стоимости ...
@Scott: Спасибо, что упомянули параметры по умолчанию в C++. Я видел их на C++, но это было в школе, и я давно о них забыл. В Ruby они называются «необязательными параметрами», а не «параметрами по умолчанию», и я связал их с динамическими языками, потому что они определяют тип параметра. В случае Ruby тип переменной нельзя изменить, но переменную можно переназначить. Я слышал о параметрах по умолчанию в VB.NET под 3.5, и мне достаточно перейти на 4.0. Однако вы правы в том, что добавление значений NULL мало.
... Я просто подумал, что стоит упомянуть о побочных эффектах упаковки структуры.
В духе 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/…
((src == null || src.Count == 0)? true: false) == (src == null || src.Count == 0)
Почему не IsNullOrEmpty<T>(), который принимает IEnumerable<T>?
Кстати, вы потеряли мой голос, добавив этот ? true : false.
Это следует разделить на два отдельных ответа. Первый в порядке (должен быть IEnumerable <T> вместо List <T>). Второй сомнительный. Я согласен с @SchlaWiener, сделайте это общим. Я бы тоже потерял catch. Если возникает исключение, позвольте вызывающей стороне решить, как его обработать.
Метод IsNullOrEmpty - хорошая идея, я использую его все время (но с IEnumerable <T>, а не List <T>) ... но я почти уверен, что он уже был опубликован. Что касается второго метода, я не вижу причин ограничивать его только FileInfo. Кроме того, передача строки с именем свойства - плохая идея: вы можете сделать то же самое с делегатом, и вы избежите накладных расходов на отражение
Вот забавный пример из нашей кодовой базы в действии. Выполните дорогостоящее перечисление с отложенным вычислением в потоке задания и отправьте результаты обратно через наблюдаемый объект.
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 в качестве имени параметра. С одной стороны, поскольку это метод расширения, вы можете рассматривать его как обычный метод класса. С другой стороны, использование (общего) ключевого слова в качестве имени параметра меня неправильно расстраивает. Хотя звучит интересно.
Изначально я подобрал @this где-нибудь в блоге или, может быть, здесь, на SO. Изначально у меня были те же опасения, но я использовал его для всех своих методов расширения в течение последних нескольких недель, и мне он очень понравился. Я думаю, что «можно думать об этом как о методе обычного класса» довольно сильно перевешивает озабоченность по поводу повторного использования ключевого слова. Я действительно думал об использовании «th» или «self» для имен, но мне особенно нравится, как действительно выскакивает @. Он постоянно напоминает мне, какой у меня метод.
Привет, Скотт, у меня действительно нет большого опыта работы с задачами или Rx, и мне трудно следить за реализацией этого метода. Полезно ли это, когда оценка отдельных элементов в последовательности является дорогостоящей (что требует async eval)? Создает ли он новый поток для каждого элемента или повторно использует тот же поток заданий по мере добавления большего количества элементов?
Это полезно, когда последовательность дорогая. Один поток извлекается из пула для асинхронного обхода перечислимого объекта. Он не возвращается до тех пор, пока перечислимое не будет завершено или не будет создано исключение. Технически, в этом примере нет необходимости в диспетчере. Я включил его, потому что я пишу много кода WPF, и это частый мой шаблон: отправить задачу, сделать что-то, опубликовать ее как наблюдаемую, отправить результаты через очередь сообщений потока пользовательского интерфейса.
Есть ли причина, по которой enumerable.ToObservable(Scheduler.TaskPool) не решает ту же проблему?
@ Ричард - нет! Не знал об этой функции. :)
Я на самом деле просто блоггинг это сегодня. Это строго типизированная реактивная оболочка для свойства 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: Разве это не верно для создания событий вручную? Решение состоит в том, чтобы использовать шаблон protected virtual void OnBar(Eventargs e), описанный здесь: msdn.microsoft.com/en-us/library/hy3sefw3%28VS.80%29.aspx.
Да, но я имею в виду, что ваше решение не работает в общем случае
Собственно, в старых версиях это тоже потокобезопасно. Я сам написал идентичные методы.
+1 - я вообще ненавижу методы расширения для Null-объектов; однако это действительно хорошее использование!
Я написал как четыре миллиона методов расширения, поэтому вот несколько из них, которые я считаю особенно полезными. Не стесняйтесь реализовывать.
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(" ", "").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 по умолчанию?
OrderBy () нельзя связать цепочкой; каждый вызов OrderBy () сортирует по одной проекции собранного типа. Вы все равно могли бы заставить его работать, если бы используемый алгоритм сортировки был стабильным, как MergeSort, но встроенный помощник сортировки является нестабильным QuickSort, поэтому нет гарантии поддержания относительного порядка при сортировке по равным условиям. Объединение в цепочку OrderBy () также будет запускать функцию O (nlogn) один раз для каждого OrderBy (); этот набор методов сортируется один раз, независимо от того, сколько терминов вы сравниваете.
Это можно улучшить, отказавшись от ToArray (). Хотя OrderBy нельзя связать цепочкой, вы должны иметь возможность связать все Comparers в IComparer<T>, который вы передаете одному OrderBy, верно?
Ну, сортировка (ЛЮБОЙ алгоритм) требует знания всего Enumerable, потому что самым последним элементом может быть тот, который идет первым в упорядоченной коллекции. OrderBy (), за кулисами, в основном делает то, что я делаю здесь; превратить исходный Enumerable в конечную коллекцию, отсортировать его, а затем передать через него.
Дело принято. :) Также: чем этот набор классов отличается от типов Enumerable.ThenBy () и IOrderedEnumerable в .NET Framework?
На самом деле это не так, как написано. Я не знал о существовании ThenBy (), когда впервые написал это. Однако есть некоторые преимущества. Поскольку это индивидуальный подход, он более расширяемый; например, вы можете добавить методы OrderUsing () и ThenUsing (), которые позволяют передавать Comparison вместо простой проекции (позволяя сортировку на основе элементов, не поддерживающих IComparable). Вы также можете настроить сортировку производительности, заменив Array.Sort, например, параллельным MergeSort.
Этот не полностью испечен, так как мы только что придумали его сегодня утром. Он сгенерирует полное определение класса для 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();
Спасибо, Джим. Продолжайте в том же духе, конструктивные комментарии, очень полезно.
@ Джон: нет проблем. Вот несколько проблем: вы создаете частный класс с общедоступными методами доступа. StringBuilder имеет метод AppendFormat, который использует средства форматирования индексов точно так же, как String.Format. В любом случае вы не должны вызывать такие заполнители шаблонов Replace on String. Вы вводите неявную строку. Единственная причина преобразовать struct.Name в псевдоним - удобочитаемость, даже тогда это не имеет особого смысла. Кроме того, IntPtr зависит от платформы, поэтому этот код работает только с 64-разрядной версией. Код, сгенерированный этим, не будет отражать фактическим кодом, который вы пытаетесь сгенерировать.
Также прошу прощения за лаконичный ответ - я был на работе. Вот отличная глава О'Рейли об размышлениях, которая, я думаю, поможет вам лучше изучить типы, для которых вы пытаетесь имитировать код: oreilly.com/catalog/progcsharp/chapter/ch18.html
Должен ли частный класс не иметь средств доступа с большей видимостью, чем частный? Я бы предпочел заставить программиста изменить видимость, чем предполагать общедоступность или что-то еще. Я понимаю, что существует appendformat, и я использую его довольно часто, но мне также нужна новая строка, и, к сожалению, у нас нет и AppendFormatLine.
Обычно я думаю, что вы полностью упускаете суть. Он не предназначен для использования в том виде, в каком он есть. Скорее всего, он будет использоваться на более индивидуальной основе. Например, у вас есть большой объект, который вы хотите представить, либо из БД, либо из класса, у которого нет доступа к источнику. Вы запускаете linqpad, чтобы сгенерировать класс, делаете несколько модов и вперед.
Кстати: Я считаю, что если у вас нет времени экстраполировать свое негативное мнение, вы должны держать его при себе до тех пор, пока вы это не сделаете. Еще раз спасибо - Джон
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.
«Нет. Если только этот класс не является вложенным, что не предлагает ваш код». Мой код ничего не предлагает. Он просто генерирует класс и заставляет программиста решить, какой должна быть доступность сгенерированного класса. «StringBuilder имеет AppendFormat () и AppendLine (); просто вызовите их оба или напишите метод расширения». Это один из способов решения этой проблемы. Я также мог бы использовать string.Format внутри AppendLine.
«Вы вводите строку неявно. Единственная причина для преобразования struct.Name в псевдоним - удобочитаемость, даже если это не имеет особого смысла». Пожалуйста, объясните это дальше. Вы хотите сказать, что я должен оставить его как «String», а не «string»? «Кроме того, IntPtr зависит от платформы, поэтому этот код работает только с 64-разрядной версией». Все хорошо, но код будет работать в 32-битном формате. Я нигде не использую IntPtr. Я просто представляю это как длинный, если он пройдет. Как и в приведенном мной примере.
Строка неявного типа с использованием ключевого слова var заставляет компилятор определять тип этой переменной. Если вы знаете, что это строка, почему бы не назвать ее строкой, чтобы компилятору не пришлось делать ничего лишнего? Комментарий о преобразовании struct.Name в псевдоним означает, что вы берете фактическую структуру из BCL (например, Int32) и переименовываете ее в строку, используя ее псевдоним (например, int). В этом нет ничего плохого, но вы можете так же легко вызвать int «Int32», длинный «Int64» или IntPtr «IntPtr». Изменив имя на псевдоним, вы ввели 64-битную ошибку.
Что касается вашего комментария о доступности на уровне класса, по умолчанию закрытой: доступ по умолчанию для класса, если вы не укажете модификатор доступа, будет internal. Я хочу сказать, что я не понимаю, почему вы «заставляете разработчика» что-то делать, генерируя код, который не компилируется ... почему бы не отказаться от модификатора доступа? Разве это не соглашение, используемое, когда Visual Studio создает для вас заглушку класса? Используя частную переменную, вы неизбежно вызовете путаницу.
Кроме того, если вы обнаружите, что используете этот код довольно часто, возможно, стоит выложить 125 долларов и .NET Reflector Pro: red-gate.com/products/reflector Затем вы сможете проанализировать внутреннее устройство точный большинства библиотек .NET. Я уверен, что вы уже слышали об этом, но другие, читающие эту ветку, могут не слышать.
Моя работа не в том, чтобы облегчить работу компилятору. Задача компилятора - облегчить мне жизнь. Я использую его, когда это возможно, если это имеет смысл (мы часто используем var на работе, и это многократно окупалось). На мой взгляд, использование var для строк совершенно нормально. Я мог бы легко оставить типы такими, какими они являются, но поскольку это предназначено для создания класса, который пользователь затем включит в свой код, он должен быть написан так, как они. Используете ли вы в своем коде «Int32 x = 5»? IntPtr не является ошибкой, как я уже говорил, он будет работать на 32-битной версии.
Я хорошо осведомлен о преимуществах рефлектора. Мы постоянно пользуемся бесплатной версией. В профессиональной версии нет ничего полезного. Что касается частной части, вы снова упускаете суть кода. Я просто оставлю все как есть, так как это никуда не денется.
Вот еще одна пара, которой я нашел бесконечное применение:
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/…
Материал, который я видел, показывающий, как вызвать лямбду, использовал .DynamicInvoke (), я не был уверен, что есть лучший способ.
Вызов DynamicInvoke () - единственное, что я сделал не так, или что-то еще? Если это было что-то еще, мне любопытно, что еще вызвало отрицательные голоса. Я ценю объяснение вызова и буду рад услышать все, что я ошибся.
Я не уверен, почему это получило пару голосов против, я не вижу ничего плохого в самом коде. Может быть, те, кто проголосовал против, думали, что эти методы не совсем подходят для C#. Вы объясняете, что заимствуете стиль Smalltalk, поэтому я не вижу, что в этом плохого.
Я не ожидаю использовать ifTrue / ifFalse в реальном коде, это было просто интересно реализовать.
Нормализация пробелов довольно полезна, особенно при работе с пользовательским вводом:
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)?
А вот расширение для вызова управления, которое я использую регулярно;
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'
});
который позволяет обновлять графический интерфейс между потоками.
Может быть, я еще не все ответы проверил.
Некоторые инструменты для 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()).
Ой, правда, забыл об этом методе. Спасибо, Пондидум! Отредактировано.
Я думал, что видел это где-то раньше, но нигде не нашел здесь подсказок. У 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 Согласен, хотя я бы назвал его GetValueOrDefault, чтобы он больше соответствовал другим аналогичным методам, таким как FirstOrDefault
Хорошо, замечание принято, я переименовал метод, как предложил @Davy8.
@ Davy8: отличное предложение; @Shaul: спасибо, что исправили (+1).
Вот еще одно контрольное расширение, которое я использовал, хотя не знаю, размещалось ли оно здесь раньше.
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)
Сравните равенство двух объектов без (обязательно) переопределения Equals или реализации IEquatable <>.
Зачем вам это нужно? Когда вы действительно хотите узнать, равны ли два объекта, но вам лень переопределить Equals(object) или реализовать IEquatable<T>. Или, что более реалистично, если у вас ужасно сложный класс, и реализация Equals вручную будет чрезвычайно утомительной, подверженной ошибкам и неинтересной в обслуживании. Это также помогает, если вы не слишком заботитесь о производительности.
В настоящее время я использую IsEqualTo по второй причине - у меня есть класс со многими свойствами, типы которых являются другими определяемыми пользователем классами, каждый из которых имеет много других свойств, типы которых являются другими определяемыми пользователем классами, до бесконечности. Добавьте кучу коллекций во многие из этих классов, и реализация Equals(object) поистине станет кошмаром.
Использование:
if (myTerriblyComplexObject.IsEqualTo(myOtherTerriblyComplexObject))
{
// Do something terribly interesting.
}
Чтобы определить равенство, я провожу многочисленные сравнения. Я изо всех сил стараюсь сделать «правильное» в «правильном» порядке. Сравнения по порядку:
Equals(object, object). Если он вернет истину, верните истину. Он вернет истину, если ссылки совпадают. Он также вернет истину, если thisObject переопределяет Equals(object).thisObject равен нулю, вернуть ложь. Если он равен нулю, дальнейшие сравнения невозможны.thisObject переопределил Equals(object), вернуть false. Поскольку он переопределяет Equals, это должно означать, что Equals был выполнен на шаге № 1 и вернул false. Если кто-то потрудился переопределить Equals, мы должны уважать это и возвращать то, что Equals возвращает.thisObject наследуется от IEquatable<T>, где otherObject может быть назначен на T, получите метод Equals(T) с использованием отражения. Вызвать этот метод и вернуть его возвращаемое значение.IEnumerable, верните, содержат ли одни и те же элементы в том же порядке, используя IsEqualTo для сравнения элементов.thisObject не имеет метода Equals, нет никакого способа реально оценить истинность двух объектов разных типов.Equals(object) - этого достаточно.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();
Отправьте любой файл в корзину:
Microsoft.VisualBasicusing 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, но это все еще хорошие расширения, и я удивлен, что их раньше никто не публиковал.
У нас есть инструмент развертывания для развертывания между средами. Поскольку файлы можно было пометить как измененные, но на самом деле не разные, я придумал следующее:
/// <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 по-прежнему очень медленный ... вместо этого вы должны создавать и кешировать динамически созданные делегаты
Если у вас персидский язык и вы должны показывать числа пользователям на персидском языке:
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 вместо конкатенации строк для повышения производительности.
Если вам нужно проверить, что ваша строка 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, на самом деле это должен быть myString.All(c => c == '0'), если вы хотите сохранить то же значение ...
Иногда нужно иметь экземпляр класса независимо от того, действителен ли он, но не равен 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;
}
Теперь первый код зафиксирован на сайте Codeplex.