您最喜欢的C#扩展方法是什么?(codeplex.com/extensionoverflow)


478

让我们列出一个答案列表,在其中发布优秀和最喜欢的扩展方法

要求是必须张贴完整的代码,并提供示例和使用说明。

基于对该主题的高度兴趣,我在Codeplex上建立了一个名为extensionoverflow的开源项目。

请标记您的答案并接受,以将代码放入Codeplex项目。

请发布完整的源代码,而不发布链接。

Codeplex新闻:

24.08.2010现在,“ Codeplex”页面位于此处:http ://extensionoverflow.codeplex.com/

11.11.2008 XmlSerialize / XmlDeserialize现在已实现并进行了单元测试

11.11.2008还有更多开发者的空间。;-) 立即加入!

11.11.2008第三位贡献者加入了ExtensionOverflow,欢迎来到BKristensen

11.11.2008 FormatWith现在已实现并进行了单元测试

2008年9月11日第二位贡献者加入了ExtensionOverflow。欢迎来到chakrit

2008年9月11日我们需要更多开发人员。;-)

2008年9月11日ThrowIfArgumentIsNull在现在实现进行单元测试 Codeplex上。


现在,第一个代码已提交到Codeplex站点。
bovium

不幸的是,Erik现在一切都在Codeplex上启动了。请仍然加入。
2008年

3
看起来还不错 关于命名静态类,我确实有意见。将它们命名为<type> Extensions并不是很有用。例如,StringExtensions同时包含格式和xml内容。我认为最好用扩展类的名称来命名类。例如UnixDateTimeConversions。您可以合理地猜测它具有在Unix时间之间来回转换的方法。只是一个想法!
ecoffey 2010年

检查此URL,以获取有关C#扩展方法的更多信息planetofcoders.com/c-extension-methods
Gaurav Agrawal,

Answers:


232
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....
}

2
好吧,如果您使用的是System.Linq,它将编译。
Ryu

11
也许“ EqualsAnyOf”会比“ In”更好的名称?
Tom Bushell 2010年

10
我不确定我是否喜欢-我喜欢的简洁In,但也许IsIn会更好。
温斯顿·史密斯,2010年

50
使用相同的Contains方法:(new [] {1,2,3})。包含(a)
Max Toro 2010年

4
我也想到了In<T>(...),发现它是标准库之外最有用的扩展方法。但是我与这个名字不一致In。方法名称应该用来描述它的作用,但In不能。我已经称之为IsAnyOf<T>(...),但我想IsIn<T>(...)也足够了。
JBSnorro

160

我的MiscUtil项目中有多种扩展方法(那里提供完整的源代码-我在这里不再重复)。我的最爱,其中一些涉及其他类别(例如范围):

日期和时间的东西-主要用于单元测试。不知道我会在生产中使用它们吗:)

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

范围和步伐-非常感谢Marc Gravell为他的操作员所做的一切:

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-在这里解释将花费很长时间,但是请进行搜索。


1
真好!您应该将其放在Google Code或CodePlex上,这样我才能向您发送一些补丁:-)我保证它将可读:-P
chakrit

3
@bovium:您已经可以看到代码了。跟随第一句话中的链接-完整的源代码在那里。
乔恩·斯基特

1
@bovium:如果您不介意,我宁愿自己做,将其放在code.google.com上并自己管理项目。显然,如果您保持适当的归属,那么您可以将其放在Codeplex上,但是除非您绝望,否则我希望自己尽快进行整理:)
Jon Skeet

1
@乔恩·斯基特 它被麻省理工学院(MIT)许可,所有人免费使用。商业或开源。为什么不联合起来为公众建立扩展方法库。
bovium

1
只是因为我在该库中做了很多其他工作。欢迎为您的项目复制所有内容,但我也希望在自己的项目中保留一份。
乔恩·斯基特

147

string.Format快捷方式:

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");

..


11
它肯定很短-但是团队中的任何新成员都不会理解。
乔恩·斯基特

3
我认为可读性在更宏大的代码方案中比一些可以快速查找/询问的速记语句更为重要。
chakrit

6
我个人想要一个单独的Formatter对象,BCL可以解析一次并重复使用的模式。这将提高可读性和性能。我问过BCL团队-我们将会看到...
乔恩·斯基特

3
这是一种扩展方法,对于团队的新成员来说当然是不可读的。我以为这个机智的东西就是这个主意?新成员还怎么知道我们有多聪明?
MarkJ

17
好的...我只是将其付诸实践并使用了.With-这样您将获得“ This is {0}”。With(“ test”),它非常易于理解并且很有意义。仅供参考
-klkitchens

89

这些有用吗?

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)函数。很好
达伦·托马斯

6
几件事情:我建议OneOf应接受any IList<T>。然后,你总是可以具有过载,需要一个paramsARG和刚刚通过的是进入IList<T>过载。我给出了一个NextBool与您的方法类似的方法(现在位于底部)CoinToss,但是给出了一个带probability参数的重载(如果我希望75%的时间发生什么,该怎么办?)。同样,这只是一个小巧的选择:您的示例代码将抛出a,NullReferenceException因为rand它从未初始化。
丹涛

3
+1我真的很喜欢,但是我更喜欢CoinToss用来实现,rng.NextDouble() < .5因为内部.Next(int)是用来实现的,.NextDouble()因此您可以节省强制转换,*和支票。
Lasse Espeholt

76
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))
{
  // ....
}

19
我喜欢这一点,但我试图确定是否将边界检查包括在最小值上,而不包括在最大值上。我想知道这是否会造成混淆。5.Between(5,10)为true,但5.Between(1,5)为false。甚至不确定同伴的内部方法是否会有所帮助。hou?
史蒂夫·海纳尔

12
“ IsBetween”这个名称更有意义吗?也可以使IsBetweenInclusive和IsBetweenExclusive。不知道哪一个默认。
fretje

2
@Steve:如果它是日期时间扩展名,则更有意义。
Joel Coehoorn

16
对我而言,这意味着:5.Between(5,10)返回false,而10.Between(5,10)也返回false。这对我来说很自然。
亚历·巴拉诺斯基

3
在我看来,很多人对自然是有不同的想法。因此,可能应该明确说明正在使用的内容(即包含与不包含),因为这可能是非常容易出错的来源。
David Miani

58

扩展方法:

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);

15
会更好,因为此IList <T>

21
只需使用集合初始化器=>var list = new List<int>{5,4,8,4,2};
Arnis Lapsa,2009年

为什么不只在您的方法中调用List <T> .AddRange(IEnumerable <T>集合)?
Rauhotz

8
@Will:实际上,最好接受ICollection<T>; 那么它也可以用于LinkedList<T>HashSet<T>,而不仅仅是索引集合。
丹涛

2
编辑以允许在.net 4.0之前的协方差
BlueRaja-Danny Pflughoeft,2010年

55

务必将其放在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.
    }
}

8
我很想打电话给第一个ToXml()(例如ToString()
Jay Bazuzi 09年

1
如果OP有意用这种方式写的话,对OP表示歉意,但是使用MemoryStreams和XmlReader / XmlWriter实在是太过分了。StringReader和StringWriter类非常适合此操作。
波特曼2009年

2
当心,这不是线程安全的。您绝对应该同步对静态序列化程序字典的访问。
Yann Schwartz,2009年

2
@ Yann,@ T,如果您仅添加“ thread static”属性,则要容易得多。然后将为每个线程创建一个新的缓存。无需同步。
Frank Krueger

1
@Jonathan C Dickinson:从此处的MSDN文档msdn.microsoft.com/zh-cn/library/…看来,所使用的构造函数(新的XmlSerializer(type))没有内存泄漏问题。所以也许不需要缓存代码?
slolife 2010年

46

IEnumerables的ForEach

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项目中


13
关于LINQ的IEnumerable <T>扩展为何不包含ForEach的帖子:stackoverflow.com/questions/317874/…– 2009
尼尔

13
使用方法之前,我建议你阅读本:blogs.msdn.com/ericlippert/archive/2009/05/18/...
jpbochi

2
@jpbochi:这只是微软的
吸引力

1
@abatishchev您的评论只是对微软的偏见。它不会使Eric编写的任何单词无效。仅仅因为他/她工作的公司,某人的论据就不会成立或无效。
jpbochi 2010年

1
顺便说一句,让我说清楚一点。我并不是说您不应该使用此ForEach扩展方法。我只是说过,在决定是否使用Eric之前,您应该考虑Eric暴露的要点。我阅读了它,因此决定不使用它。您可以随意使用代码进行任何操作。
jpbochi 2010年

43

我的转化扩展程序可让您执行以下操作:

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:class)。我还提供了静默异常模型和典型的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分钟,然后才把毛巾扔了。


64
就个人而言,我不喜欢尝试/捕获以确定结果的代码。对于在预期逻辑IMO之外发生的错误,应使用try / catch。hmmmmm
Pure.Krome,

如果我不希望您使用该代码,则不会发布它!:)
TheSoftwareJedi

终于有些看不见的东西。我喜欢。:)
Arnis Lapsa,2009年

8
您至少应该更改“ catch”子句,以仅捕获ChangeType()无法“转换”引用时将引发的那些异常。我认为您不希望任何OutOfMemoryException,ExecutionEngineException,ThreadAbortException或类似的东西被视为转换错误。否则,这些事情将很难跟踪错误。
Christian.K,2009年

2
我相信ToOrNull其行为与ToOrDefault(例如,如果您致电ToOrDefault与之转换失败的引用类型,它将返回null)。但更重要的是,对我来说,这似乎有些多余,因为它var s = myObject as string完成了与var s = myObject.ToOrNull<string>()- 相同的操作,但又不必潜在地抓住InvalidCastException。我想念什么吗?
丹涛

43

我有一个记录异常的扩展方法:

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

它的用法是这样的:

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

[抱歉发布两次;第二个是更好的设计:-)]


2
应该读取public static void Log(this Exception obj){}吗?
克里斯S

我认为这对BCL或第三方异常很有用,但是如果您滚动自己的异常类型,则可以将日志记录放在基本异常类中。这样,您就不必记住调用Log()了。
si618 2010年

38
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);
    }
}

将字符串解析为枚举很有用。

public enum TestEnum
{
    Bar,
    Test
}

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

功劳归于 斯科特·多尔曼

-编辑Codeplex项目-

我问过斯科特·多曼(Scott Dorman),他是否愿意我们在Codeplex项目中发布他的代码。这是我从他那里得到的答复:

感谢您对SO帖子和CodePlex项目的注意。我已就你对这个问题的回答投票赞成。是的,该代码有效地存在于CodeProject开放许可(http://www.codeproject.com/info/cpol10.aspx)下的公共领域中)。

我将其包含在CodePlex项目中没有任何问题,如果您想将我添加到项目中(用户名是sdorman),我将添加该方法以及一些其他枚举帮助器方法。


这个枚举解析方案一直都在出现……必须将其放入我的lib中:-)
chakrit

哇,我一直在写将字符串映射到枚举的方法(刚开始使用.NET)。谢谢,这绝对有帮助!
凯文

4
您可能还考虑命名此ToEnum <>(),因为它位于对象之后。
尼尔

需要注意的是Enum.TryParse <T>已被添加到网络4.0 - blogs.msdn.com/bclteam
丹DIPLO

1
我认为这种方法不应该使用Trim。整理输入内容应该是调用方的责任。
CodesInChaos

32

我发现这非常有用:

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

您可以在CodePlex上使用它。


2
有人能对我们这个天赋不足的人解释一下吗?
jpbochi 2010年

hahaha刚读了这篇文章(上面Joel的评论)-真是有趣,但是回过头来(在接收端,而不是Paula端),回头真是有趣!曾经让一个承包商来从事一个项目的工作,我是设计者/负责开发的人-她不在我的直接控制之下,但是从我的团队工作清单中分配了工作。老板称赞她表现出色(甚至后来再次聘用她作为开发负责人!)。从来没有想到,她编写或设计的每段代码都没有付诸生产,所有这些都必须由我的团队完全重新编写!
Wolf5370'5

31

DateTimeExtensions

例子:

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

5
我建议将“ SetTime”重命名为“ WithTime”,因为它实际上并未将其设置为现有值。尼斯,否则。
乔恩·斯基特

28
DateTime.Now.First()-首先是什么?仅从示例代码中可以明显看出。
Mackenir

2
非常好。但是同意这些名字可能会好得多。
2008年

如果该方法有据可查,则DateTime.Now.First在Intellisense中将足够清楚。
Ryan Lundy

29

gitorious.org/cadenza是我所见过的一些最有用的扩展方法的完整库。


12种相当基本的扩展方法。我有点不知所措。
Mackenir

(我说的是发布的版本,而不是您需要使用源代码控制来获得的版本)
mackenir

28

这是我经常用于演示文稿格式化的一个。

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

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

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

哎呀,Pokemon异常处理将隐藏诸如ThreadAbortException等问题。请捕获一些特定的信息。
JBRWilkinson

28

这是罗马数字的往返。不经常使用,但可能很方便。用法:

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

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

来源:

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

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

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

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

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

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

            var length = value.Length;

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

            var total = 0;
            var i = length;

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

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

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

                total += digit;
            }

            return total;
        }

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

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

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

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

            return sb.ToString();
        }
    }

这让我想起了Python的PEP 313,这是一个愚人节的玩笑,包括在Python罗马数字字面值:python.org/dev/peps/pep-0313
torial

25

处理尺寸的便捷方法:

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

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

我认为这确实是糟糕的编码风格。应该改用常量,而不要混淆逻辑。
xxbbcc 2012年

24

对于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上使用这些...


1
如前所述,这仅适用于WinForms。它可以与WPF一起使用,但存在一些问题(如msdn.microsoft.com/en-us/library/…上有关WPF的注释中所述)。我发现的WPF最佳解决方案在geekswithblogs.net/lbugnion/archive/2009/09/05/…中进行了描述(尽管由于它是静态属性,所以它实际上不能作为扩展方法。)
scobi

23

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项目上使用此代码。


我也喜欢这个,乔恩(Jon)也有,我用的是Umbrella的类似东西,可以放下“ ArgumentIs”部分。
cfeduke

是的 这也是一个粗略的扩展方法:)
Pure.Krome,

3
如果仅将ArgumentNullException-constructor与1个字符串参数一起使用,则该参数只能是参数名称,而不是错误消息。因此,您的代码应如下所示:if(obj == null)throw new ArgumentNullException(parameterName);
汤米·卡利尔

我会default(T)为此使用并删除课程要求。
Joel Coehoorn

1
@Joel:本机类型的非默认值是合法参数,而不是null值。对我来说,检查null比检查默认值更有意义。当然,我只是通过说Require.ThatArgument(input != null)或来概括整个想法Require.ThatArgument(personId > 0)。它不需要那么多的代码,它要灵活得多,并且读起来很漂亮。当您要自定义错误消息或异常本身时,我还有其他覆盖功能的函数。
StriplingWarrior

22

移到C#时,我想念Visual Basic的With语句,所以就这样:

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项目


4
只是一个猜测,但请考虑一下如果您的T是一个结构会发生什么。
Rauhotz

5
我还尽可能使用c#3.0属性初始化程序语法来获得相同的结果。
史蒂夫

3
@chakrit,这是一个例子。仅当创建对象时才适用。Button n = new Button {Name =“ Button1”,Width = 100,Height = 20,Enabled = true};
史蒂夫(Steve)

1
当您有很多事件要挂起时,这将很有用,因为C#的属性初始化器语法不支持事件。
加布

1
在属性初始化器之外这也是很有用的,因为您只能在创建新对象时使用它们。此扩展可以在以前创建的对象上工作。
布雷迪·莫里茨

18

接受camelCaseWord或PascalCaseWord并将其“单词化”,即camelCaseWord => camel Case Word

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中的Aggregate对于性能非常不利,因为它会创建许多字符串实例。为什么不使用word.Substring(1)呢?
Thomas Levesque

17

我发现这有帮助

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

它将删除调用代码中的空检查。你现在可以做

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

是的,如果有人忘记了“空对象模式”,则此方法很有用。集合永远不能为空。
Pavel Hodek

16

将双精度转换为使用指定区域性格式化的字符串:

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

13
您应该使用Decimal表示货币,否则会出现取整问题
Andrew Bullock

如何在参数中使用Enum而不是纯字符串
Rulas

15

以下是适应Rick Strahl的代码扩展方法(以及注释),以防止您每次将其转换为字符串时都不得不猜测或读取字节数组或文本文件的字节顺序标记。

该代码段使您可以简单地执行以下操作:

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

如果发现任何错误,请添加评论。随时将其包含在Codeplex项目中。

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

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

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

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

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

非常有用的方法,但我不认为应该是扩展方法。
Pop Catalin 09年

如果你正在写一个文本编辑器,它可能令一个扩展方法,但我同意大部分时间它可能不会超过一个静态的私有方法
克里斯小号

15

这是我今天刚创建的。

// 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;

1
如果需要thingy.NullOr(t => t.Count)Count一个int 在哪里?您应该返回default(TReturn)而不是null,这样就不需要class约束,它也适用于值类型
Thomas Levesque 2010年

2
应该要求TIn是一个类,否则整个扩展方法都没有意义(值类型不能为null)。您的t.Count示例确实适用于上述扩展方法。你能再看看吗?
scobi 2010年

@Scott:这是解决常见问题的有用方法。但是,我相信TReturn elseValue = default(TReturn)仅适用于.NET 4.0吗?我有3.5 SP1,但从未见过这种构造(我的编译器也没有)。我只是将其移至方法内部。但是,一个问题是,将一个可为null的类型装箱到用于该方法的对象会产生意外的结果(0 vs预期的null)。
Jim Schubert

@Jim:default(T)自VS2005以来一直使用该关键字,但是我认为默认参数是.NET 4的新功能。解决它的简单方法应该是拥有两个变体,一个变体采用参数,而另一个不变体。我将答案更新为与CLR 2.0兼容。关于拳击-这就是重点default。对于值类型,它将是0初始化的数据,对于所有引用类型,它将为null。值类型的TReturn在整个函数中应始终保持未装箱状态。
scobi 2010年

@Scott:我的问题是关于默认参数的,我只在像Ruby这样的动态语言中才看到过。关于可空类型的观点是,返回x.Value应返回空(例如,如果为int?空)或返回值(如果int?具有值)。返回0何时int? x = null传递并装箱到对象是一种奇怪的情况。对于这种特定情况,我已经在类似流利的nhibernate和linfu(我认为)之类的库中看到了类似的可空类型检查,使您可以像以前建议的那样删除类约束。
Jim Schubert

14

“请在接受的答案上打上标记,以将代码放入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>?因此,您只需要返回新的String(input.Reverse());即可。
伊恩·加洛韦

使用StringBuilder的实现应该更快。
CodesInChaos

1
@CodeInChaos stackoverflow.com/questions/228038中的基准测试表明StringBuilder较慢。
迈克尔·斯托姆

你是对的。看起来线程安全性要求(可能是为了确保ToString返回的字符串的不变性)使StringBuilder的运行速度大大降低。
CodesInChaos

2
希望您不会遇到任何替代或组合字符。
dalle

14

从MySqlDataReader提取值时,我厌倦了繁琐的null检查,因此:

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一起使用。


hangy和Joe都对如何执行此操作有很好的评论,此后,我就有机会在不同的上下文中实现类似的操作,因此这里是另一个版本:

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);
}

2
这也应作为IDataReader的扩展方法。
hangy

2
实际上,将IDataRecord类型的“ this”参数设为最大兼容性。在此版本中,我有一个重载,该重载使用fieldName版本调用的序数。保存“ GetOrdinal”,然后按名称查找。
乔尔·穆勒

有一个正确的实施,可以处理任何类型的值: rabdullin.com/journal/2008/12/6/...
里纳特阿卜杜林

感谢Rinat,我实际上已经将其归结为一个通用方法-请参见stackoverflow.com/questions/303287
Adam Lassek,

所有这些方法似乎都是不需要的,因为您可以使用as关键字从读取器获取允许空值的值。如果将空合并??运算符与as运算符结合使用,则甚至可以具有非空默认值,以直接进入值类型。见stackoverflow.com/questions/746767/...
stevehipwell

14

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上。


13

这是针对MVC的,它增加了<label />为每个Html变量中可用的变量生成标记的能力ViewPage。希望它对尝试开发类似扩展的其他人有用。

采用:

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

输出:

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

码:

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

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

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

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

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

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

        tag.MergeAttributes(htmlAttributes);

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

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

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

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

签出MvcContrib.FluentHtml
Arnis Lapsa,2009年

这可能应该与Literal复制。
马克·赫德

12

转这个:

DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT @param";

DbParameter param = command.CreateParameter();
param.ParameterName = "@param";
param.Value = "Hello World";

command.Parameters.Add(param);

...变成这样:

DbCommand command = connection.CreateCommand("SELECT {0}", "Hello World");

...使用此扩展方法:

using System;
using System.Data.Common;
using System.Globalization;
using System.Reflection;

namespace DbExtensions {

   public static class Db {

      static readonly Func<DbConnection, DbProviderFactory> getDbProviderFactory;
      static readonly Func<DbCommandBuilder, int, string> getParameterName;
      static readonly Func<DbCommandBuilder, int, string> getParameterPlaceholder;

      static Db() {

         getDbProviderFactory = (Func<DbConnection, DbProviderFactory>)Delegate.CreateDelegate(typeof(Func<DbConnection, DbProviderFactory>), typeof(DbConnection).GetProperty("DbProviderFactory", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true));
         getParameterName = (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), typeof(DbCommandBuilder).GetMethod("GetParameterName", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null));
         getParameterPlaceholder = (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), typeof(DbCommandBuilder).GetMethod("GetParameterPlaceholder", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null));
      }

      public static DbProviderFactory GetProviderFactory(this DbConnection connection) {
         return getDbProviderFactory(connection);
      }

      public static DbCommand CreateCommand(this DbConnection connection, string commandText, params object[] parameters) {

         if (connection == null) throw new ArgumentNullException("connection");

         return CreateCommandImpl(GetProviderFactory(connection).CreateCommandBuilder(), connection.CreateCommand(), commandText, parameters);
      }

      private static DbCommand CreateCommandImpl(DbCommandBuilder commandBuilder, DbCommand command, string commandText, params object[] parameters) {

         if (commandBuilder == null) throw new ArgumentNullException("commandBuilder");
         if (command == null) throw new ArgumentNullException("command");
         if (commandText == null) throw new ArgumentNullException("commandText");

         if (parameters == null || parameters.Length == 0) {
            command.CommandText = commandText;
            return command;
         }

         object[] paramPlaceholders = new object[parameters.Length];

         for (int i = 0; i < paramPlaceholders.Length; i++) {

            DbParameter dbParam = command.CreateParameter();
            dbParam.ParameterName = getParameterName(commandBuilder, i);
            dbParam.Value = parameters[i] ?? DBNull.Value;
            command.Parameters.Add(dbParam);

            paramPlaceholders[i] = getParameterPlaceholder(commandBuilder, i);
         }

         command.CommandText = String.Format(CultureInfo.InvariantCulture, commandText, paramPlaceholders);

         return command;
      }
   }
}

更多ADO.NET扩展方法:DbExtensions

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.