Hello World
Spiga

常用辅助方法收集

2009-08-25 15:11 by 老赵, 5717 visits

在项目里经常会出现一些常用的,好用的扩展方法,因此也想到把它收集起来。原本打算放在gist.github上,不过似乎那里管理功能较为薄弱,因此只适合零碎的代码片断,不适合长期地维护一些代码。而放在MSDN Code Gallary上又嫌较为简单,不成一个完整的示例——自然更别说是作为开源项目放在CodePlex上了。思来想去,还是放在博客上较为合适。

如果您有什么好的辅助方法,也可以告诉我,希望可以成为一套丰富的辅助方法,简化我们的开发生活。

字典相关

public static class DictionaryExtensions
{
    public static TDictionary CopyFrom<TDictionary, TKey, TValue>(
        this TDictionary source, 
        IDictionary<TKey, TValue> copy)
        where TDictionary : IDictionary<TKey, TValue>
    {
        foreach (var pair in copy)
        {
            source.Add(pair.Key, pair.Value);
        }

        return source;
    }

    public static TDictionary CopyFrom<TDictionary, TKey, TValue>(
        this TDictionary source, 
        IDictionary<TKey, TValue> copy, 
        IEnumerable<TKey> keys)
        where TDictionary : IDictionary<TKey, TValue>
    {
        foreach (var key in keys)
        {
            source.Add(key, copy[key]);
        }

        return source;
    }

    public static TDictionary RemoveKeys<TDictionary, TKey, TValue>(
        this TDictionary source, 
        IEnumerable<TKey> keys)
        where TDictionary : IDictionary<TKey, TValue>
    {
        foreach (var key in keys)
        {
            source.Remove(key);
        }

        return source;
    }

    public static IDictionary<TKey, TValue> RemoveKeys<TKey, TValue>(
        this IDictionary<TKey, TValue> source, 
        IEnumerable<TKey> keys)
    {
        foreach (var key in keys)
        {
            source.Remove(key);
        }

        return source;
    }
}

枚举相关

字符串相关

其他

AttachDataExtensions

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

43 条回复

  1. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-25 15:54:00


    我记得 new dictionary<>(sourcedic) 就可以copy了,不用一个一个add

    但是老赵你是不是为了照顾 SortedList阿

  2. Astar
    *.*.*.*
    链接

    Astar 2009-08-25 15:58:00

    注释说明一下好点。

  3. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-25 16:00:00

    我来贴一个 linklist 枚举器
    可以指定从任意节点向前/向后枚举 不用每次从头来


       static public  class LinkListNodeExtention 
        {
            public enum EnumerateDirection
            {
                Forward=0,
                Backward
            }
    
            static public IEnumerable<LinkedListNode<T>> GetMidwayEnumerable<T>(this LinkedListNode<T> currentNode, EnumerateDirection direction, bool selectSelf)
            {
                var tmpNode = currentNode;
                if (tmpNode == null)  yield break  ;
                switch (direction )
                {
                    case EnumerateDirection.Forward :
    
                       
                        if (selectSelf) yield return tmpNode ;
    
                        while (tmpNode.Next !=null )
                        {
                            tmpNode = tmpNode.Next;
                            yield return tmpNode;
                        }
                        yield break;
                    case EnumerateDirection .Backward :
       
                        if (selectSelf) yield return tmpNode;
    
                        while (tmpNode.Previous != null)
                        {
                            tmpNode = tmpNode.Previous ;
                            yield return tmpNode;
                        }
    
                        yield break;
                }
            
            }
    
    
        }
    

  4. 李永京
    *.*.*.*
    链接

    李永京 2009-08-25 16:00:00

    老赵怎么不弄个sf Repository呢

  5. 老赵
    admin
    链接

    老赵 2009-08-25 16:01:00

    @Astar
    视情况而定吧,目前这些还比较简单。

  6. 老赵
    admin
    链接

    老赵 2009-08-25 16:03:00

    @韦恩卑鄙
    我这个通用性更好啊,例如任何实现IDictionary<TKey, TValue>的类型都可以使用。
    还有就是我可以这样:
    source.CopyFrom(dict1).CopyFrom(dict2).CopyFrom(dict3);

  7. 老赵
    admin
    链接

    老赵 2009-08-25 16:03:00

    @李永京
    sf和codeplex又有什么区别呢?

  8. 李永京
    *.*.*.*
    链接

    李永京 2009-08-25 16:05:00

    Jeffrey Zhao:
    @李永京
    sf和codeplex又有什么区别呢?


    http://sourceforge.net/ 上面的Repository库啊,小弟简写而已

  9. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-25 16:06:00

    @Jeffrey Zhao
    你是坏人我早就说过

  10. anitzlz
    *.*.*.*
    链接

    anitzlz 2009-08-25 16:07:00

    向高手学习

  11. 老赵
    admin
    链接

    老赵 2009-08-25 16:14:00

    李永京:
    http://sourceforge.net/ 上面的Repository库啊,小弟简写而已


    我知道啊,我是说我看不出sf和codeplex有什么区别,我既然不放在codeplex上,自然也不会放在sf上了。

  12. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-25 16:14:00

    顺便问为啥放在这一栏阿 丰富丰富转到前面去吧

  13. 老赵
    admin
    链接

    老赵 2009-08-25 16:18:00

    @韦恩卑鄙
    目前没啥可丰富的啊,有机会我把以前项目中用过的整理一下。

  14. 李永京
    *.*.*.*
    链接

    李永京 2009-08-25 16:19:00

    @Jeffrey Zhao
    我答非所问了,哎哎,没区别

  15. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-25 16:20:00

    那我得经常来刷刷

  16. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-08-25 16:30:00

    韦恩卑鄙:楼上 不会显示传递泛型参数的


    我已经意识到了,所以删掉了。

  17. 老赵
    admin
    链接

    老赵 2009-08-25 16:30:00

    @Ivony...
    Dictionary有什么特别的高性能方法吗?刚看了MSDN没发现特别的,ArrayCopy是什么?
    TDictionary的意义在于,我写ASP.NET MVC应用程序的时候,就可以在RouteValueDictionary上使用了。
    所以为了更为通用,还是基于IDictionary<,>写吧。

  18. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-25 16:30:00

    Ivony...:

    韦恩卑鄙:楼上 不会显示传递泛型参数的


    我已经意识到了,所以删掉了。


    那我也删

  19. 老赵
    admin
    链接

    老赵 2009-08-25 16:32:00

    @韦恩卑鄙
    干,我就不删了。

  20. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-08-25 16:42:00

    哎,反正我现在是没办法看源代码,我只是猜测。。。

    因为Dictionary内部似乎就是两个数组么,_keys和_values,如果有原生的方法,我想我会实现成把这两个数组Copy一下吧。
    Array.Copy和foreach的性能差距应该还是有区别的,Array.Copy是一个native方法,我要是微软的话,应该会弄个Memory Copy来实现。

  21. 老赵
    admin
    链接

    老赵 2009-08-25 16:50:00

    @Ivony...
    哦哦,Dictionary没有特别的方法的,其实我的方法就是个“方便”目的,呵。
    而且其实字典是个哈希表,也算复杂的数据结构,里面还要划分bucket等等,不仅仅是两个数组。
    说道MemCopy,在GC下做这种东西还是挺束手束脚的,不过微软已经有类似的方法了
    就是Buffer.BlockCopy,高性能的数组复制方法。

  22. vczh[未注册用户]
    *.*.*.*
    链接

    vczh[未注册用户] 2009-08-25 17:07:00

    在你的硬盘上创建一个Class Library,全部丢进去,以后直接引用即可……

  23. vczh[未注册用户]
    *.*.*.*
    链接

    vczh[未注册用户] 2009-08-25 17:10:00

    如果是一些只能用源代码的地方,其实我一直都是建立了一个目录,然后分类放进去的。我用这种办法维护了一个不大不小的专门用来处理字符串、界面和数据的库。

  24. 老赵
    admin
    链接

    老赵 2009-08-25 17:10:00

    @vczh
    事实上我觉得这样不方便,例如没发四处访问,没法做一点点修改,发布成类库又要考虑版本升级……不如直接列代码,谁要谁copy。

  25. RednaxelaFX
    *.*.*.*
    链接

    RednaxelaFX 2009-08-25 17:18:00

    我想起我的FxUtil……贴些上来。刚才发回复的时候说我超过字数限制了,只好拆成两个回复。

    字典相关:

    using System;
    using System.Collections.Generic;
    
    namespace FxUtil {
        public static class DictionaryUtility {
            #region Extension methods for IDictionary<TKey, TValue>
    
            public static TValue GetValueOrDefault<TKey, TValue>( this IDictionary<TKey, TValue> dict, TKey key ) {
                TValue result;
                dict.TryGetValue( key, out result );
                return result;
            }
    
            #endregion
        }
    }
    

    IDictionary<,>上的索引器和TryGetValue都有麻烦的地方,索引器会抛异常,TryGetValue非要预先声明变量(就用不了var了),所以加了这么个方法。

    还有像是给数字添加扩展方法:
    using System;
    using System.Collections.Generic;
    
    namespace FxUtil {
        public static class NumericUtil {
            public static IEnumerable<int> Upto( this int start, int end ) {
                if ( start > end ) throw new ArgumentException( "start is greater than end" );
                return UptoCore( start, end );
            }
    
            private static IEnumerable<int> UptoCore( int start, int end ) {
                for ( var current = start; current <= end; ++current ) {
                    yield return current;
                }
            }
    
            public static IEnumerable<int> Downto( this int start, int end ) {
                if ( start < end ) throw new ArgumentException( "start is less than end" );
                return DowntoCore( start, end );
            }
    
            private static IEnumerable<int> DowntoCore( int start, int end ) {
                for ( var current = start; current >= end; --current ) {
                    yield return current;
                }
            }
    
            public static IEnumerable<int> Times( this int time ) {
                if ( time < 0 ) throw new ArgumentOutOfRangeException( "time must be non-negative" );
                return TimesCore( time );
            }
    
            private static IEnumerable<int> TimesCore( int time ) {
                for ( var i = 0; i < time; ++i ) {
                    yield return i;
                }
            }
    
            public static void Times( this int time, Action action ) {
                if ( time < 0 ) throw new ArgumentOutOfRangeException( "time must be non-negative" );
                if ( null == action ) return;
                
                for ( var i = 0; i < time; ++i ) {
                    action( );
                }
            }
    
            public static void Times( this int time, Action<int> action ) {
                if ( time < 0 ) throw new ArgumentOutOfRangeException( "time must be non-negative" );
                if ( null == action ) return;
                
                for ( var i = 0; i < time; ++i ) {
                    action( i );
                }
            }
    
            public static IEnumerable<T> Times<T>( this int time, Func<T> func ) {
                if ( time < 0 ) throw new ArgumentOutOfRangeException( "time must be non-negative" );
                if ( null == func ) throw new ArgumentNullException( "func must not be null" );
                
                return TimesCore( time, func );
            }
    
            private static IEnumerable<T> TimesCore<T>( int time, Func<T> func ) {            
                for ( var i = 0; i < time; ++i ) {
                    yield return func( );
                }
            }
    
            public static IEnumerable<T> Times<T>( this int time, Func<int, T> func ) {
                if ( time < 0 ) throw new ArgumentOutOfRangeException( "time must be non-negative" );
                if ( null == func ) throw new ArgumentNullException( "func must not be null" );
                
                return TimesCore( time, func );
            }
    
            private static IEnumerable<T> TimesCore<T>( int time, Func<int, T> func ) {
                for ( var i = 0; i < time; ++i ) {
                    yield return func( i );
                }
            }
    
            public static bool IsEven( this int self ) {
                return ( self & 1 ) == 0;
            }
    
            public static bool IsOdd( this int self ) {
                return ( self & 1 ) != 0;
            }
        }
    }
    

  26. RednaxelaFX
    *.*.*.*
    链接

    RednaxelaFX 2009-08-25 17:18:00

    这样就可以写更顺口的代码,像是:

    1.Upto(5, () => Console.WriteLine("Hello FX"));
    // 或者
    1.Upto(5)
        .Process(() => Console.WriteLine("Hello FX"))
        .ExecuteQuery();
    

    Process和ExecuteQuery是另外两个扩展方法,没写出来。实现很简单,Process就是一般说的ForEach,只不过不想跟List<T>上的ForEach冲突所以换了个名字;ExecuteQuery是强制求值用的,可以想像为Enumerable.ToList<T>()或者ToArray<T>()的别名。

    另外注意到我上面代码凡是用到generator的地方都是用一个public的方法包住了private的generator。这是为了能及时检查参数是否合适而做的work-around。要注意generator是懒惰的,在第一次调用MoveNext()之前不会执行任何代码,包括可能存在的检查参数的代码。

    还有像这种比较恶搞的:
    public static Dictionary<string, T> Hash<T>( params Func<string, T>[ ] args ) {
        var items = new Dictionary<string, T>( );
        foreach ( var func in args ) {
            var item = func( null );
            items.Add( func.Method.GetParameters( )[ 0 ].Name, item );
        }
        return items;
    }
    

    可以这样用:
    var dic = Hash(
        Jef => new { FirstName = "Jeffrey", LastName = "Zhao" },
        FX => new { FirstName = "Kris", LastName = "Mok" });
    

    于是能用类似Ruby的hash语法来创建Dictionary<string,T>的实例,而且还能支持匿名类型……注意到key类型一定是string,而且key的值就是lambda的箭头左边的那个标识符,所以无法写为标识符的字符串就没办法用在这里了。纯娱乐 ^ ^

    话说还有一堆用了lambda来替代反射的辅助方法,不过我写的版本功能还不太好,仅供一笑:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    
    namespace FxUtil {
        public static class ReflectionUtil {
            #region methods to retrive reflection data through expression trees
    
            // the C# compiler gets to use internal ways of getting members,
            // such as ldtoken + GetMethodFromHandle()
    
            public static MethodInfo GetMethod<T>( Expression<Action<T>> expr ) {
                var methodCall = expr as MethodCallExpression;
                if ( null == methodCall ) {
                    throw new ArgumentException( "Expected method call" );
                }
                return methodCall.Method;
            }
    
            public static PropertyInfo GetProperty<TClass, TValue>( Expression<Func<TClass, TValue>> expr ) {
                var memberExpression = expr.Body as MemberExpression;
                if ( memberExpression == null || !( memberExpression.Member is PropertyInfo ) ) {
                    throw new ArgumentException( "Expected property expression" );
                }
                return ( PropertyInfo ) memberExpression.Member;
            }
    
            public static FieldInfo GetFieldInfo<TClass, TValue>( Expression<Func<TClass, TValue>> expr ) {
                var memberExpression = expr.Body as MemberExpression;
                if ( memberExpression == null || !( memberExpression.Member is FieldInfo ) ) {
                    throw new ArgumentException( "Expected property expression" );
                }
                return ( FieldInfo ) memberExpression.Member;
            }
    
            #endregion
        }
    }
    

    用法是类似:
    PropertyInfo prop = ReflectionUtil.GetProperty( ( DateTime dt ) => dt.Day );
    Console.WriteLine( "{0}.{1}", prop.ReflectedType, prop.Name );
    

    (这有个问题:GetProperty只支持有getter的属性。不过只有setter没getter的属性也很少,就懒得改了……)

    嗯我的FxUtil还有些别的……不过好久没整理了,暂时就贴这么些吧 ^ ^

  27. 王仕超
    *.*.*.*
    链接

    王仕超 2009-08-25 17:22:00

    我来贴一个数据转换成可为空的Extention,

     public static class DataConvertExtention
        {       
            public static Nullable<Decimal> ToNullableDecimal(this object obj)
            {
                if (obj == null)
                {
                    return null;
                }
                else if (obj.Equals(DBNull.Value))
                {
                    return null;
                }
                else if ((obj.GetType() == typeof(string) && string.IsNullOrEmpty(obj.ToString()))
                    || (obj.GetType() == typeof(char) && string.IsNullOrEmpty(obj.ToString())))
                {
                    return null;
                }
                else
                {
                    return Convert.ToDecimal(obj);
                }
            }
        
            public static Nullable<double> ToNullableDouble(this object obj)
            {
                if (obj == null)
                {
                    return null;
                }
                else if (obj.Equals(DBNull.Value))
                {
                    return null;
                }
                else if ((obj.GetType() == typeof(string) && string.IsNullOrEmpty(obj.ToString()))
                    || (obj.GetType() == typeof(char) && string.IsNullOrEmpty(obj.ToString())))
                {
                    return null;
                }
                else
                {
                    return Convert.ToDouble(obj);
                }
            }
            public static Nullable<int> ToNullableInt32(this object obj)
            {
    
                if (obj == null)
                {
                    return null;
                }
                else if (obj.Equals(DBNull.Value))
                {
                    return null;
                }
                else if ((obj.GetType() == typeof(string) && string.IsNullOrEmpty(obj.ToString()))
                    || (obj.GetType() == typeof(char) && string.IsNullOrEmpty(obj.ToString())))
                {
                    return null;
                }
                else
                {
                    return Convert.ToInt32(obj);
                }
            }
            
            public static int ToInt32(this object obj)
            {
                return Convert.ToInt32(obj);
            }      
            public static Nullable<DateTime> ToNullableDateTime(this object obj)
            {
                if (obj == null)
                {
                    return null;
                }
                else if (obj.Equals(DBNull.Value))
                {
                    return null;
                }
                else if ((obj.GetType() == typeof(string) && string.IsNullOrEmpty(obj.ToString()))
                    || (obj.GetType() == typeof(char) && string.IsNullOrEmpty(obj.ToString())))
                {
                    return null;
                }
                else
                {
                    return Convert.ToDateTime(obj);
                }
            }
    
            public static Nullable<bool> ToNullableBoolean(this object obj)
            {
                if (obj == null)
                {
                    return null;
                }
                else if (obj.Equals(DBNull.Value))
                {
                    return null;
                }
                else if ((obj.GetType() == typeof(string) && string.IsNullOrEmpty(obj.ToString()))
                    || (obj.GetType() == typeof(char) && string.IsNullOrEmpty(obj.ToString())))
                {
                    return null;
                }
                else
                {
                    return Convert.ToBoolean(obj);
                }
            } 
        }
    

  28. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-08-25 21:51:00

    做论坛的时候写了个IEnumerable的Paging扩展,有机会翻出来贴上。

  29. LisaStone[未注册用户]
    *.*.*.*
    链接

    LisaStone[未注册用户] 2009-08-25 21:55:00

    后面三类的代码怎么看不到涅?

  30. 老赵
    admin
    链接

    老赵 2009-08-25 21:59:00

    @Ivony...
    好像就是把pageIndex和pageSize计算一下,然后.Skip().Take()?

  31. 老赵
    admin
    链接

    老赵 2009-08-25 21:59:00

    @LisaStone
    还没有收集。

  32. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-08-25 22:08:00

    @Jeffrey Zhao
    是啊,但在做数据绑定的时候却方便很多,有空把分页浏览控件完善好,配合起来就好玩了。

    好像我还对List和Array做了特别的处理,即不用Skip而是直接索引定位。

  33. 老赵
    admin
    链接

    老赵 2009-08-25 22:41:00

    @Ivony...
    不错不错,呵呵。

  34. RednaxelaFX
    *.*.*.*
    链接

    RednaxelaFX 2009-08-25 22:59:00

    Ivony...:
    哎,反正我现在是没办法看源代码,我只是猜测。。。

    因为Dictionary内部似乎就是两个数组么,_keys和_values,如果有原生的方法,我想我会实现成把这两个数组Copy一下吧。
    Array.Copy和foreach的性能差距应该还是有区别的,Array.Copy是一个native方法,我要是微软的话,应该会弄个Memory Copy来实现。


    Array.Copy确实是用类似memcpy的方式实现的,比手工循环复制高效,减少了许多开销(特别是数组大的时候)。举例来说,假如数组元素是引用类型,那么给数组的元素赋值就得经过write barrier;手工复制的话每复制一个元素都要经过一次write barrier,而Array.Copy可能可以只经过一次……

  35. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-26 01:05:00

    但是dictionary 是hashtable 并不是两个数组 以上

  36. 非空
    *.*.*.*
    链接

    非空 2009-08-26 08:26:00

  37. 老赵
    admin
    链接

    老赵 2009-08-26 09:27:00

    @非空
    哈哈,Tree Visitor。
    我们有卑鄙兄,不要这个。

  38. 蛙蛙王子
    *.*.*.*
    链接

    蛙蛙王子 2009-08-26 09:27:00

    强烈支持老赵的这个帖子,大家都贡献一下

  39. 麒麟.NET
    *.*.*.*
    链接

    麒麟.NET 2009-08-26 09:36:00

    啧啧,扩展方法真好!

  40. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-08-26 10:38:00

    脸红了 那我还是贴个连接

    http://www.cnblogs.com/waynebaby/archive/2009/08/16/1546980.html
    广义树枚举器

  41. Pag
    *.*.*.*
    链接

    Pag 2009-08-26 16:16:00

        public class DiagnosticsHelper
        {
            public static bool TraceInfo(object message)
            {
                if (!string.IsNullOrEmpty(Convert.ToString(message)))
                {
                    System.Diagnostics.Trace.WriteLine(message);
                }
                return false;
            }
        }
    


    在Debug过程中可以在任何需要输出Trace信息的代码行上添加条件断点,在Condition中写上DiagnosticsHelper.TraceInfo("xxx")即可输出Trace信息。

  42. CoolCode
    *.*.*.*
    链接

    CoolCode 2009-08-26 20:43:00

    这个话题很好,期待后续!

  43. RednaxelaFX
    *.*.*.*
    链接

    RednaxelaFX 2009-09-14 14:47:00

    看到了这个:http://elevate.codeplex.com/ 可以关注和借鉴

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我