(第一篇) 一步一步带你了解linq to Object

要想学好linq to object 我们必须要先学习lambda 表达式,学习lambda 表达式呢我们必须了解匿名函数和匿名类,学习匿名函数,我们必须学会委托,这是本文的宗旨。下面开始第一步。在第一步开始之前,我们做点准备工作,建立一个学生类和一个班级类,类结构如下

   public class Student
    {
        public int Id { get; set; }
        public int ClassId { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        public void Study()
        {
            Console.WriteLine("{0} {1}跟着Eleven老师学习.net高级开发", this.Id, this.Name);
        }
    }

    public class Class
    {
        public int Id { get; set; }
        public string ClassName { get; set; }
    }

在准备一个基础数据类

 public class LinqShow
    {
        /// <summary>
        /// 准备数据
        /// </summary>
        /// <returns></returns>
        public List<Student> GetStudentList()
        {
            #region 初始化数据
            List<Student> studentList = new List<Student>()
            {
                new Student()
                {
                    Id=1,
                    Name="打兔子的猎人",
                    ClassId=2,
                    Age=35
                },
                new Student()
                {
                    Id=1,
                    Name="Alpha Go",
                    ClassId=2,
                    Age=23
                },
                 new Student()
                {
                    Id=1,
                    Name="白开水",
                    ClassId=2,
                    Age=27
                },
                 new Student()
                {
                    Id=1,
                    Name="狼牙道",
                    ClassId=2,
                    Age=26
                },
                new Student()
                {
                    Id=1,
                    Name="Nine",
                    ClassId=2,
                    Age=25
                },
                new Student()
                {
                    Id=1,
                    Name="Y",
                    ClassId=2,
                    Age=24
                },
                new Student()
                {
                    Id=1,
                    Name="小昶",
                    ClassId=2,
                    Age=21
                },
                 new Student()
                {
                    Id=1,
                    Name="yoyo",
                    ClassId=2,
                    Age=22
                },
                 new Student()
                {
                    Id=1,
                    Name="冰亮",
                    ClassId=2,
                    Age=34
                },
                 new Student()
                {
                    Id=1,
                    Name="瀚",
                    ClassId=2,
                    Age=30
                },
                new Student()
                {
                    Id=1,
                    Name="毕帆",
                    ClassId=2,
                    Age=30
                },
                new Student()
                {
                    Id=1,
                    Name="一点半",
                    ClassId=2,
                    Age=30
                },
                new Student()
                {
                    Id=1,
                    Name="小石头",
                    ClassId=2,
                    Age=28
                },
                new Student()
                {
                    Id=1,
                    Name="大海",
                    ClassId=2,
                    Age=30
                },
                 new Student()
                {
                    Id=3,
                    Name="yoyo",
                    ClassId=3,
                    Age=30
                },
                  new Student()
                {
                    Id=4,
                    Name="unknown",
                    ClassId=4,
                    Age=30
                }
            };
            #endregion
            return studentList;
        }

    }

简单了解委托

1. 委托是什么?

其实,我一直思考如何讲解委托,才能把委托说得更透彻。说实话,每个人都委托都有不同的见解,因为看问题的角度不同。个人认为,可以从以下2点来理解:

 (1) 从数据结构来讲,委托是和类一样是一种用户自定义类型

(2) 从设计模式来讲,委托(类)提供了方法(对象)的抽象。

既然委托是一种类型,那么它存储的是什么数据?

我们知道,委托是方法的抽象,它存储的就是一系列具有相同签名和返回回类型的方法的地址。调用委托的时候,委托包含的所有方法将被执行。

2. 委托类型的定义

委托是类型,就好像类是类型一样。与类一样,委托类型必须在被用来创建变量以及类型对象之前声明。

delegate void MyDel(int x);

委托类型声明:

(1) 以deleagate关键字开头。

(2)返回类型+委托类型名+参数列表。

3. 声明委托变量

MyDel del1,del2;

4. 初始化委托变量

(1) 使用new运算符

new运算符的操作数的组成如下:

  • 委托类型名
  • 一组圆括号,其中包含作为调用列表中的第一个成员的方法的名字。方法可以是实例方法或静态方法。
del1 = new MyDel( 实例方法名 );
del2 = new MyDel( 静态方法名 );

(2)使用快捷语法

快键语法,它仅由方法说明符构成。之所以能这样,是因为在方法名称和其相应的委托类型之间有隐式转换。

del1 = 实例方法名;
del2 = 静态方法名;

5. 赋值委托

由于委托是引用类型,我们可以通过给它赋值来改变包含在委托变量中的方法地址引用。旧的引用会被垃圾回收器回收。

MyDel del;
del = 实例方法名; //委托初始化
del = 静态方法名;//委托重新赋值,旧的引用将被回收

6. 委托调用

委托调用跟方法调用类似。委托调用后,调用列表的每个方法将会被执行。

在调用委托前,应判断委托是否为空。调用空委托会抛出异常,这是正常标准调用

if(null != del)
{
     del.Invoke();//委托调用
}

在net 1.1的时代,我们是使用Invoke()方法类调用委托的。但是微软在net 2.0 的时候,我们可以这么去调用委托 直接 del();

if(null != del)
{
     del();//委托调用
}

7. 综合练习

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
    class Program
    {
        public delegate void meathed1();//1 委托的声明   委托是一种类型
        public delegate int meathed2();//2 可以声明在类的内部,也可以声明在类的外部
        public delegate void meathed3(int id, string name);
        public delegate string meathed4(string msg);
        static void Main(string[] args)
        {
            meathed1 me1 = new meathed1(m1);
            meathed2 me2 = new meathed2(m2);
            meathed3 me3 = new meathed3(m3);
            meathed4 me4 = new meathed4(m4);
            me1.Invoke();
            int result=   me2.Invoke();
            Console.WriteLine(result);
            me3(1, "wbc");
            Console.WriteLine(me4("您吃了吗"));
            Console.Read();

        }
        public static void m1() {
            Console.WriteLine("我是无参数无返回值的方法");
        }
        public static int m2()
        {
            Console.WriteLine("我是无参数有返回值的方法~~~~~~~~~");
            return 123;
        }
        public static void m3(int id, string name)
        {
            Console.WriteLine($"我是有参数无返回值的方法,id={id},name={name}");
        }
        public static string m4(string sparams)
        {
            Console.WriteLine($"我是无参数无返回值的方法 ,参数{sparams}");
            return "参数返回了";
        }
    }
}

8. net 框架提供的自带委托

8.1.Func委托类型

Func是有返回值的泛型委托,可以没有参数,但最多只有16个参数,并且必须要有返回值。

Func<string, int> funcDelegate = new Func<string, int>();

2.Action委托类型
Action是没有返回值的泛型委托,可以没有参数,但最多只有16个参数。

 Action<string, string> method = new Action<string, string>();

关于泛型这里就不过多介绍了,本人前边写了一篇比较详细。建议大家以后使用委托的时候,尽量使用net 类库自带的委托。

匿名方法&lambda表达式

我们看了上面的委托,发现写起来好麻烦,并且用起来特别的不方便,有没有更简便的方法呢?可不可以把我们的方法写到委托里面呢?答案是肯定的;这种写法我们称为匿名方法。写法如下:

 Action<string> method = new Action<string>(delegate(string name){
                Console.WriteLine(name);
            });
            method.Invoke("wbc");
            Console.Read();

但是这种写法还不够简便,在.net 2.0的时候,我们发现1.1的时候在频繁的使用委托,微软做了很大的改进和更新,新的写法如下

 Action<string> method = new Action<string>(name =>
                Console.WriteLine(name)
            );
            method.Invoke("wbc");
            Console.Read();

在2.0里面,我们去掉了delegate关键字,如果子有一个参数的时候,连括号也省略了,用"=>"指向方法体,这个尖叫号等号我们读做“goes to”,如果方法只有一句话,连花括号也省略了。参数类型直接就可以推算出来。看一个复杂的案列

  Func<string,int,string> method = new Func<string, int, string>((name,age) => {
                return $"{name}今年{age}岁了";
            }

            );
         string result=   method.Invoke("wbc",0);
            Console.WriteLine(result);
            Console.Read();

其实我们的匿名方法就是lambda 表达式。

匿名类

有了匿名方法一定会有匿名类,在net 3.0的时候才有的匿名类,我们看下在没有匿名类的时候,我们是如何去写的。

 object student = new  {
                id=123,
                name="1234"
            };
            Console.WriteLine(student);
            Console.Read();

这种写法只能输出 “{ id = 123, name = 1234 }”这种结果的Json 数据,并且我们在程序中不能使用student.id,会报错。看下匿名写法

        var model = new
            {
                Id = 1,
                Name = "Alpha Go",
                Age = 21,
                ClassId = 2
            };
            Console.WriteLine(model.Age);
            Console.Read();

我们来看下var 关键字,其实var 关键字就是一个语法糖,我们不必过于在意,这里就不过多的演示了,我们的根本目的是linq to object

  1. 必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式:    var s;    s = “abcd”;//这种写法是不允许的  2. 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。  3. var要求是局部变量。   4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。

这里在简要扩展点技术点“dynamic”,dynamic的用法和object差不多,区别是dynamic在编译的时候才声明的一个类型,但是本人建议在程序开发中尽量避开这个关键字,后续MVC 中在详细介绍,dynamic是net 40里面的关键字。

扩展方法

扩展方法必须建立在静态类中的静态方法,且第一个参数前边要加上this 关键字,使用扩展方法必须引用扩展方法所在的类的命名空间,如果想要全局使用,可以不带命名空间。个人建议程序中的扩展方法尽量要带上命名空间。我们来看下扩展方法的使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
    class Program
    {

        static void Main(string[] args)
        {

            int? a = null;
            Console.WriteLine(Helper.ToInt(a));//常规写法输出
            Console.WriteLine(a.ToInt());//扩展方法输出
            if ("1qazwsx1qazwsx1qazwsx1qazwsx".ToLengthInCount(16))
            {
                Console.WriteLine("密码长度应当小于16个字符");
            }
            Console.Read();
        }

    }
    public static class Helper
    {
        /// <summary>
        /// 验证一个数字是否为空,为空返回-1
        /// 扩展方法:静态类里的静态方法,第一个参数类型前面加上this
        /// </summary>
        /// <param name="iParameter"></param>
        /// <returns></returns>
        public static int ToInt(this int? iParameter) {
            return iParameter ?? -1;
        }
        /// <summary>
        /// 验证一个字符串长度是否大指定长度
        ///
        /// </summary>
        /// <param name="sParameter"></param>
        /// <returns></returns>
        public static bool ToLengthInCount(this string sParameter, int iCount)
        {
            return sParameter.Length >= iCount ?   true : false;
        }
    }

}

这里简要说明下,扩展方法的方法名和类内不得方法名相同,会优先选择类内部的方法,这是以就近原则,去执行的,关于就近原则,请看本人java 相关博客,有介绍

linq to object 

1. 什么是linq to object ?

LINQ即Language Integrated Query(语言集成查询),LINQ是集成到C#和Visual Basic.NET这些语言中用于提供查询数据能力的一个新特性。linq to object 就是对象查询体系。

  注:LINQ(发音为Link)

我们来看一个简单的列子,用到了我们前边准备的两个类。我们要取出学生年龄小于30岁的,我们如果不会linq to object 的写法如下

 LinqShow linq = new LinqShow();
            List<Student> studentList =   linq.GetStudentList();//获取基础数据
            List<Student> studentListLessThan30 = new List<Student>();
            foreach (var item in studentList)
            {
                if (item.Age < 30)
                {
                    Console.WriteLine($"name={item.Name},age={item.Age}");
                    studentListLessThan30.Add(item);
                }
            }
            Console.Read();

我们可不可以使用委托去计算呢??

  LinqShow linq = new LinqShow();
            List<Student> studentList =   linq.GetStudentList();//获取基础数据
            List<Student> studentListLessThan30 = new List<Student>();
            //Func<Student, bool> predicate = s => s.Age < 30;//写法一,
            //var list = studentList.Where<Student>(predicate);//linq 

            var list = Enumerable.Where(studentList, s => s.Age < 30);//使用lambda 表达式写法
            foreach (var item in list)
            {
                Console.WriteLine($"Name={item.Name} Age={item.Age}");
            }
            Console.Read();

两段代码我就不比较了,这里我们只来剖析下 studentList 的Where方法,我们可以转移到定义看下,其实where 里面的参数,如下图

  我们会发现,这不List<T>自带的一个方法,而是一个扩展方法,这个扩展方法在”System.Linq“命名空间的“public static class Enumerable”类下,也就是说根据扩展方法的特点,我们只要引用了“System.Linq”命名空间,就能使用这个扩展方法,这个扩展方法真的第二个参数是一个Func<TSource, bool> 委托,这个委托有一个TSource泛型参数,一个bool返回值。这就说明了我们上面的lambda 表达式是成立的。

  继续剖析,我们接受的时候,没有使用List<Student>接收返回的集合,这是因为我们的linq 只有遇到循环的时候或遇到ToList<T>()的时候才会去迭代,这里涉及到迭代器的问题,后续我们会详细讲解迭代器的使用,或者大家去网上查询下设计模式迭代器。这么说,我们很难理解。我们通过一个案列来分析,自己写一个for 循环的扩展方法,对比下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
    class Program
    {

        static void Main(string[] args)
        {
            LinqShow linq = new LinqShow();
            List<Student> studentList =   linq.GetStudentList();//获取基础数据
            //var list = Enumerable.Where(studentList, s => s.Age < 30);//使用lambda 表达式写法,
            Console.WriteLine($"-------------------------linq写法-----------------------------------");
            var list = studentList.Where( s => {
                Console.WriteLine($"我的年龄是{s.Age}");
               return s.Age < 30;
            });//使用lambda 表达式写法,这里在每一次计算,我们都输出一个我的年龄是,这里不再使用 Enumerable 去调用where ,反正是扩展方法,用法是一样的
            foreach (var item in list)
            {
                Console.WriteLine($"Name={item.Name} Age={item.Age}");
            }
            Console.WriteLine($"-------------------------自定义扩展方法写法-----------------------------------");
            var Wbclist = studentList.WbcWhere(s => {
                Console.WriteLine($"Wbc我的年龄是{s.Age}");
                return s.Age < 30;
            });// 自定义扩展方法写法 这里不再使用 Enumerable 去调用where
            foreach (var item in Wbclist)
            {
                Console.WriteLine($"Name={item.Name} Age={item.Age}");
            }
            Console.Read();
        }

    }
    public static class Helper
    {
        /// <summary>
        /// 自定义扩展方法计算集合
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static IEnumerable<TSource> WbcWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            //常规情况下数据过滤
            List<TSource> studentListLessThan30 = new List<TSource>();
            foreach (var student in source)
            {
                //if (item.Age < 30)
                if (predicate(student))
                {
                    studentListLessThan30.Add(student);
                }
            }
            return studentListLessThan30;

        }
        /// <summary>
        /// 验证一个数字是否为空,为空返回-1
        /// 扩展方法:静态类里的静态方法,第一个参数类型前面加上this
        /// </summary>
        /// <param name="iParameter"></param>
        /// <returns></returns>
        public static int ToInt(this int? iParameter) {
            return iParameter ?? -1;
        }
        /// <summary>
        /// 验证一个字符串长度是否大指定长度
        ///
        /// </summary>
        /// <param name="sParameter"></param>
        /// <returns></returns>
        public static bool ToLengthInCount(this string sParameter, int iCount)
        {
            return sParameter.Length >= iCount ?   true : false;
        }
    }

}

我们来看下输入结果

通过执行结果,我们会发现,自定义的扩展方法是循环一遍,统一输出的,没有延迟,而linq 的写法有延迟。这也就是我们linq 核心之一 “linq 是有延迟的”。这里特别说明,linq 的延迟加载和EF ,ORM 的延迟加载,不是一会事,这里说的延迟,指的是当用到某条数据的时候,才会去查询。

2. 高逼格装X时刻

我们以llambda表达式去写linq 称为 查询运算符写法,下边我们 使用 查询表达式去写,注意区分什么是查询表达式,什么是查询运算符写法,我面试的很多人都说反了。

案列一 多条件过滤筛选数据

  LinqShow linq = new LinqShow();
            List<Student> studentList =   linq.GetStudentList();//获取基础数据
                //查询运算符
                var list1 = studentList.Where<Student>(s => s.Age < 30 && s.Name.Length > 3);//陈述句
                foreach (var item in list1)
                {
                    Console.WriteLine("Name={0}  Age={1}", item.Name, item.Age);
                }

                //查询表达式
                Console.WriteLine("********************");
                var list2 = from s in studentList
                           where s.Age < 30 && s.Name.Length > 3
                           select s;
                foreach (var item in list2)
                {
                    Console.WriteLine("Name={0}  Age={1}", item.Name, item.Age);
                }

            Console.Read();

我们会发现,表达式的写法是不是很像sql ,语法以from  对象 ... in .. where  select 对象  ,是不是很高大上,够逼咯吧!!!!!!,下边在简单介绍几个案列

案列二将筛选数据付给匿名类或者实体类

 {
                    Console.WriteLine("********************");
                    var list = studentList.Where<Student>(s => s.Age < 30)
                                         .Select(s => new
                                         {
                                             IdName = s.Id + s.Name,
                                             ClassName = s.ClassId == 2 ? "高级班" : "其他班"
                                         });
                    foreach (var item in list)
                    {
                        Console.WriteLine("Name={0}  Age={1}", item.ClassName, item.IdName);
                    }
                }
                {
                    Console.WriteLine("********************");
                    var list = from s in studentList
                               where s.Age < 30
                               select new
                               {
                                   IdName = s.Id + s.Name,
                                   ClassName = s.ClassId == 2 ? "高级班" : "其他班"
                               };

                    foreach (var item in list)
                    {
                        Console.WriteLine("Name={0}  Age={1}", item.ClassName, item.IdName);
                    }
                }

案列三 排序分页

   var list = studentList.Where<Student>(s => s.Age < 30)
                                         .Select(s => new
                                         {
                                             Id = s.Id,
                                             ClassId = s.ClassId,
                                             IdName = s.Id + s.Name,
                                             ClassName = s.ClassId == 2 ? "高级班" : "其他班"
                                         })
                                         .OrderBy(s => s.Id)//首先升序排序
                                         .OrderByDescending(s => s.ClassId)//首先降序排序
                                         .ThenBy(s => s.ClassId)//升序降序排序以后再排序
                                         .Skip(2)//跳过  分页
                                         .Take(3)//获取数据
                                         ;
                    foreach (var item in list)
                    {
                        Console.WriteLine($"Name={item.ClassName}  Age={item.IdName}");
                    }

案列四 关联查询

首先添加班级数据

 List<Class> classList = new List<Class>()
                {
                    new Class()
                    {
                        Id=1,
                        ClassName="初级班"
                    },
                    new Class()
                    {
                        Id=2,
                        ClassName="高级班"
                    },
                    new Class()
                    {
                        Id=3,
                        ClassName="微信小程序"
                    },
                };

表达式写法

  var list = from s in studentList
                           join c in classList on s.ClassId equals c.Id
                           select new
                           {
                               Name = s.Name,
                               CalssName = c.ClassName
                           };
                foreach (var item in list)
                {
                    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
                }

算数运输符写法

  var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
                {
                    Name = s.Name,
                    CalssName = c.ClassName
                });
                foreach (var item in list)
                {
                    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
                }

在linq  中没有右链接,如果要写右链接,只能反过来写,这里不过多去写案列了,这种案列网上很多,一会总结过后,分享几篇

总结及推荐

1.匿名函数只能使用在委托中

2.var 关键字的特点,如何使用var 实现匿名类。

3. lambda 表达式

4.扩展方法

5.运算符linq  写法

6.表达式linq 写法

7 linq 的延迟加载原理

分享 1linq 案列 ,很全面的

分享2  linq  标准查询操作符  很全面的 共五篇

原文地址:https://www.cnblogs.com/wangbaicheng1477865665/p/8503014.html

时间: 2024-08-01 10:42:18

(第一篇) 一步一步带你了解linq to Object的相关文章

一步一步带你入门MySQL中的索引和锁 (转)

出处: 一步一步带你入门MySQL中的索引和锁 索引 索引常见的几种类型 索引常见的类型有哈希索引,有序数组索引,二叉树索引,跳表等等.本文主要探讨 MySQL 的默认存储引擎 InnoDB 的索引结构. InnoDB的索引结构 在InnoDB中是通过一种多路搜索树——B+树实现索引结构的.在B+树中是只有叶子结点会存储数据,而且所有叶子结点会形成一个链表.而在InnoDB中维护的是一个双向链表. 你可能会有一个疑问,为什么使用 B+树 而不使用二叉树或者B树? 首先,我们知道访问磁盘需要访问到

U-BOOT-2016.07移植 (第一篇) 初步分析

U-BOOT-2016.07移植 (第一篇) 初步分析 目录 U-BOOT-201607移植 第一篇 初步分析 目录 编译和移植环境 更新交叉编译工具 1 下载arm-linux-gcc 443 2 安装arm-linux-gcc 443 安装环境Ubuntu 910 下载u-boot-201607并解压 分析顶层Makefile 1 找出目标依赖关系 2 总结 初次编译u-boot 1 配置 2 编译 分析u-boot启动流程 1 分析startS 2 分析crt0S 3 总结 1. 编译和移

第一篇 SQL Server安全概述

本篇文章是SQL Server安全系列的第一篇,详细内容请参考原文. 面对当今复杂的攻击SQL Server有你需要的一切来保护你的服务器和数据.但在你能有效地使用这些安全功能之前,你需要了解你所面临的威胁和一些基本的安全概念.本系列的第一篇将讲解基础知识,可以充分利用SQL Server中的安全功能而不是浪费时间在不能保护你的数据被威胁的功能上.Relational databases are used in an amazing variety of applications with co

小白触摸MVC第一篇

首先非常感谢喜科堂提供的入门视频,对我帮助很大谢谢您们. 第一篇实现功能:基于MVC实现我的小计算器 使用版本:Vs2012 第一步新建MvcDemo -----------------------------如果没有出现以下问题请跳过------------------- 新建ASP.NET MVC项目时,出现下面的错误: 解决方法 菜单栏-工具-扩展和更新 在左侧选择“联机”,然后搜索“NuGet Package Manager”,在搜索结果里选择“NuGet Package Manager

深入理解javascript作用域系列第一篇——内部原理

× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原理更为重要.本文是深入理解javascript作用域系列的第一篇——内部原理 内部原理分成编译.执行.查询.嵌套和异常五个部分进行介绍,最后以一个实例过程对原理进行完整说明 编译 以var a = 2;为例,说明javasc

从0开始搭建SQL Server AlwaysOn 第一篇(配置域控)

从0开始搭建SQL Server AlwaysOn 第一篇(配置域控) 网上的 AlwaysOn可以说是非常的多,也可以说是非常的千篇一律,而且很多都是搭建非常顺利的,没有坑的,难道搭建 AlwaysOn真的可以这麽顺利吗?????? 由于公司使用的是最新的Windows Server 2012 R2,网上用的都是Windows Server 2008 R2 ,2012 R2和2008 R2在故障转移集群界面菜单和AD 服务管理工具 已经有较大变化,有一些步骤跟Windows Server 20

《你的灯亮着吗》第一篇总结

第一篇通过在雷龙塔大楼里电梯不够用的故事引出问题,对于没有经验的问题解决者们,大都是急于寻找解决办法,而不是先给出要解决的问题下定义.这种情况下,很难找得到真正解决问题的方法.对于一个准问题解决者说,从一开始考虑问题就想得比较全面,要清楚谁有问题和问题的本质是什么.要知道你自己的问题是什么,是你期望的东西与你体验的东西之间的差别,但是真正的问题是幻觉中的问题.通过多方面的考虑,最终解决了问题.而最后在梁龙先生的葬礼上信差彼得与雷龙塔隔壁公司的Corvair先生通过交流达成了一个更好的建议,在这次

第一篇:无角牛MVC通用后台数据库设计

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} Normal 0 false 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {

第一篇 读《构建之法》有感

第一篇                           读<构建之法>有感 读了<构建之法>前两章之后,我倍有感触.一二章主要讲的是现代软件工程的概论以及个人技术和流程.这本书主要的思想是“做中学”.首先讲出来软件=软件工程+程序,做出来的软件有各种不同用处,软件工程从刚开始的探索阶段,到成熟阶段经历了一个漫长的时间.从软件的用途来讲软件企业=软件+商业模式,当然软件企业还需要各方面的支持工作软件工程决定了软件的质量,商业模式决定了一个软件的成败.软件从业人员和软件企业的道德操