编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]

简介: 原文:编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]前言     软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类。不管是数组还是集合类,它们都有各自的优缺点。
原文: 编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]

前言

    软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类。不管是数组还是集合类,它们都有各自的优缺点。如何使用好集合是我们在开发过程中必须掌握的技巧。不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行。

  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

  建议16、元素数量可变的情况下不应使用数组

  建议17、在多数情况下使用foreach进行循环遍历

  建议18、foreach不能代替for

  建议19、使用更有效的对象和集合初始化

建议16、元素数量可变的情况下不应使用数组

  在C#中,数组一旦被创建,长度就不能改变。如果我们需要一个动态且可变长度的集合,就应该使用ArrayList或List<T>来创建。而数组本身,尤其是一维数组,在遇到要求高效率的算法时,则会专门被优化以提升其效率。一维数组也成为向量,其性能是最佳的,在IL中使用了专门的指令来处理它们。

  从内存使用的角度来讲,数组具有以下特点:

  1、数组在创建时被分配了一段固定长度的内存。

  2、如果数组元素是值类型,则每个元素的长度等于相应的值类型的长度

  3、如果数组的元素是引用类型,则每个元素的长度为该引用类型的IntPtr.Size。

  4、数组的存储结构一旦被分配,就不能再变化。

  而ArryaList是这样的:

  1、ArrayList是链表结构,可以动态增减内存空间。

  2、如果ArrayList存储的是值类型,则会为每个元素增加12字节的空间,其中4字节用于对象引用,8字节是元素装箱时引入的对象头。

  而List<T>是ArrayList的泛型实现,它省去了拆箱和装箱带来的开销。

如果一定要动态改变数组的长度,一种方法是将数组转换为ArrayList或List<T>,如下面的代码所示:

            ///定义一个一维数组
            int[] iArr = { 0,1,3,4,6,7,9};
            ///将数组转换为ArrayList
            ArrayList arrayListInt = ArrayList.Adapter(iArr);
            arrayListInt.Add(11);
            ///将数组转换为List<T>
            List<int> listInt = iArr.ToList<int>();
            listInt.Add(11);

  还有一种方法是用数组的复制功能。数组继承自System.Array,抽象类System.Array提供了一些有用的实现方法,其中就包含了Copy方法,它负责将一个数组的内容复制到另外一个数组中。无论是哪种方法,改变数组长度就相当于重新创建了一个数组对象。

  为了让数组看上去本身就具有动态改变长度的功能,还可以创建一个名为ReSize的扩展方法。

    public static class ClassForExtensions
    {
        public static Array ReSize(this Array array,int newSize)
        {
            Type t = array.GetType().GetElementType();
            Array newArray = Array.CreateInstance(t, newSize);
            Array.Copy(array, 0, newArray, 0, Math.Min(array.Length, newSize));
            return newArray;
        }
    }

调用方式如下:

        static void Main(string[] args)
        {
            int[] iArr = { 0,1,3,4,6,7,9};
            iArr = (int[])ClassForExtensions.ReSize(iArr, 20);
            Console.ReadLine();
        }

下面我们来对比一下性能,先来看代码:

    class Program
    {
        static void Main(string[] args)
        {
            ResizeArray();
            ResizeList();
            Console.ReadLine();
        }

        public static void ResizeArray()
        {
            int[] iArr = {0,1,3,4,6,8 };
            Stopwatch watch = new Stopwatch();
            watch.Start();///用于测量时间间隔
            iArr = (int[])iArr.ReSize(10);
            watch.Stop();///
            Console.WriteLine("ResizeArray:{0}", watch.Elapsed);
        }

        public static void ResizeList()
        {
            List<int> iArr = new List<int>(new int[] { 0, 1, 3, 4, 6, 8, 9 });
            Stopwatch watch = new Stopwatch();
            watch.Start();
            iArr.Add(0);
            iArr.Add(0);
            iArr.Add(0);
            watch.Stop();
            Console.WriteLine("ResizeList:{0}", watch.Elapsed);
        }
    }

Main函数中主要是调用,自己定义的两个方法,第一个是重新设置数组的长度,第二个是设置List<T>的长度,通过运行时间进行测量:

严格意义上讲,List<T>不存在改变长度的说法,此处主要是来进行对比一下,对List<T>设置长度,并且进行赋值,即便是这样,在时间效率上ResizeList比ResizeArray要高很多很多。

建议17、在多数情况下使用foreach进行循环遍历

 这里关于如何针对集合才能使用foreach进行遍历我刚刚写了一篇有关IEnumerable和IEnumerator两个接口的文章,有兴趣的话可以看一下。http://www.cnblogs.com/aehyok/p/3641193.html

感觉使用foreach进行循环遍历,总共有三个好处吧:

1、提供了比较简单、简洁的语法。

2、自动将代码置入try-finally块

3、若类型实现IDispose接口,foreach会在循环结束后自动调用Dispose方法

建议18、foreach不能代替for

foreach存在一个问题是:它不支持循环时对集合进行增删操作。我们来看一下简单的例子:

            List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
            foreach (int item in list)
            {
                list.Remove(item);
                Console.WriteLine(item.ToString());
            }
            Console.ReadLine();

一起看一下执行结果:

那么下面我们来使用for进行尝试:

            List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
            for (int i = 0; i < list.Count(); i++)
            {
                list.Remove(list[i]);
            }
            Console.ReadLine();

  进行删除肯定是没问题的。但是要仔细看一下,比如它第一次删除索引0的时候,也就是删除了1,那么它会立即重新调整索引,然后第二次删除的时候,删除的不是2,而是3这个项。那么最终运行完发现还剩余两项

 foreach循环使用了迭代器进行集合的遍历,它在FCL提供的迭代器内部维护了一个对集合版本的控制。那么什么是集合版本呢?简单的说,其实它就是一个整型的变量,任何对集合的增删操作都会使版本号加1。foreach循环会调用MoveNext方法来遍历元素,在MoveNext方法内部会进行版本号的检测,一旦检测到版本号有变动,就会抛出InvalidOperationException异常。

  如果使用for循环就不会带来这样的问题。for直接使用所引器,它不对集合版本号进行判断,所以不存在因为集合的变动而带来的异常(当然,超出索引长度这种情况除外)。

  索引,因为版本检测的缘故,foreach循环并不能带起for循环。 

建议19、使用更有效的对象和集合初始化

   对象初始化设定项支持可以直接在大括号中对自动实现的属性进行赋值。

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person() { Name = "aehyok", Age = 25 };
            Console.ReadLine();
        }
    }

以往只能依靠构造方法传值进去,或者在对象构造完毕后对属性进行赋值。现在这些步骤简化了,初始化设定项实际相当于编译器在对象生成后对属性进行了赋值。

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person() { Name = "Kris", Age = 22 };
            List<Person> personList = new List<Person>()
            { 
                new Person() { Name = "aehyok", Age = 25 },
                person,
                null
            };
            Console.ReadLine();
        }
    }

使用集合的初始化设定项,编译器会在集合对象创建完毕后对集合调用Add方法。上面这段代码展示了如何在初始化语句中创建一个新对象或一个现有对象,以及一个null值。

 不过,初始化设定项绝不仅仅是为了对象和集合初始化的方便,它更重要的作用是为LINQ查询中的匿名类型进行属性的初始化。由于LINQ查询返回的集合中匿名类型的属性都是只读的,如果需要为匿名类型属性赋值,或者增加属性,只能通过初始化设定项来进行。初始化设定项还能为属性使用表达式。

来看一段代码:

List<Person> lst = new List<Person>()
 { 
    new Person(){ Age = 10,Name="Tommy"},
    new Person(){ Age = 20,Name="Sammy"}
 };
var entity = from p in lst
             select new { p.Name, AgeScope = p.Age > 10 ? "Old" : "Young" };
foreach (var item in entity)
{
   Response.Write(string.Format("name is {0},{1}", item.Name, item.AgeScope));
}

AgeScope 属性是经过计算得出的,有了如此方便的初始化方式,使得代码更加优雅灵活。

 

目录
相关文章
|
1月前
|
存储 C# 索引
C# 一分钟浅谈:数组与集合类的基本操作
【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
61 2
|
13天前
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue&lt;T&gt;`和`ConcurrentDictionary&lt;TKey, TValue&gt;`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
26 1
|
19天前
|
C# 容器
C#中的命名空间与程序集管理
在C#编程中,`命名空间`和`程序集`是组织代码的关键概念,有助于提高代码的可维护性和复用性。本文从基础入手,详细解释了命名空间的逻辑组织方式及其基本语法,展示了如何使用`using`指令访问其他命名空间中的类型,并提供了常见问题的解决方案。接着介绍了程序集这一.NET框架的基本单位,包括其创建、引用及高级特性如强名称和延迟加载等。通过具体示例,展示了如何创建和使用自定义程序集,并提出了针对版本不匹配和性能问题的有效策略。理解并善用这些概念,能显著提升开发效率和代码质量。
33 4
|
25天前
|
Linux C# 开发者
Uno Platform 驱动的跨平台应用开发:从零开始的全方位资源指南与定制化学习路径规划,助您轻松上手并精通 C# 与 XAML 编程技巧,打造高效多端一致用户体验的移动与桌面应用程序
【9月更文挑战第8天】Uno Platform 的社区资源与学习路径推荐旨在为初学者和开发者提供全面指南,涵盖官方文档、GitHub 仓库及社区支持,助您掌握使用 C# 和 XAML 创建跨平台原生 UI 的技能。从官网入门教程到进阶技巧,再到活跃社区如 Discord,本指南带领您逐步深入了解 Uno Platform,并提供实用示例代码,帮助您在 Windows、iOS、Android、macOS、Linux 和 WebAssembly 等平台上高效开发。建议先熟悉 C# 和 XAML 基础,然后实践官方教程,研究 GitHub 示例项目,并积极参与社区讨论,不断提升技能。
35 2
|
1月前
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
16 2
|
2月前
|
数据安全/隐私保护 C# UED
利用 Xamarin 开展企业级移动应用开发:从用户登录到客户管理,全面演示C#与Xamarin.Forms构建跨平台CRM应用的实战技巧与代码示例
【8月更文挑战第31天】利用 Xamarin 进行企业级移动应用开发能显著提升效率并确保高质量和高性能。Xamarin 的跨平台特性使得开发者可以通过单一的 C# 代码库构建 iOS、Android 和 Windows 应用,帮助企业快速推出产品并保持一致的用户体验。本文通过一个简单的 CRM 示例应用演示 Xamarin 的使用方法,并提供了具体的代码示例。该应用包括用户登录、客户列表显示和添加新客户等功能。此外,还介绍了如何增强应用的安全性、数据持久化、性能优化及可扩展性,从而构建出功能全面且体验良好的移动应用。
35 0
|
2月前
|
缓存 NoSQL Redis
【Azure Redis 缓存】C#程序是否有对应的方式来优化并缩短由于 Redis 维护造成的不可访问的时间
【Azure Redis 缓存】C#程序是否有对应的方式来优化并缩短由于 Redis 维护造成的不可访问的时间
|
5月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
168 3
|
5月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
181 3