08
8

一、简介

在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆。我想下面的代码能证实这点。下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=5.答案是6个First不仅被编译,并都获得正确答案,且他们的结果一样。如果你对此感到困惑,那么请继续看这篇文章。

class Customer
{
    public int ID { get; set; }
    public static bool Test(Customer x)
    {
        return x.ID == 5;
    }
}
...
List<Customer> custs = new List<Customer>();
custs.Add(new Customer() { ID = 1 });
custs.Add(new Customer() { ID = 5 });
 
custs.First(new Func<Customer, bool>(delegate(Customer x) { return x.ID == 5; }));
custs.First(new Func<Customer, bool>((Customer x) => x.ID == 5));
custs.First(delegate(Customer x) { return x.ID == 5; });
custs.First((Customer x) => x.ID == 5);
custs.First(x => x.ID == 5);
custs.First(Customer.Test);

二、什么是委托?

现在你定义一个处理用户订单的购物车ShoppingCart类。管理层决定根据数量,价格等给客人折扣。做为其中的一部分,他们已经实现了处理订单时你要考虑一方面。不用考虑过多,你简单声明一个变量来保存有“吸引力的折扣”(magicDisCount),然后实现逻辑。

class Program {
    static void Main(string[] args)  {
        new ShoppingCart().Process();
    }
class ShoppingCart {
    public void Process() {
        int magicDiscount = 5;
        // ...
    }
}

第二天,异想天开的管理层决定根据购买时间调整折扣。这个很简单,但需要你改动一点代码。

class ShoppingCart {
    public void Process() {
        int magicDiscount = 5;
        if (DateTime.Now.Hour < 12) {
            magicDiscount = 10;
        }
    }
}

接下来一段时间里,管理层又反复添加更多的折扣逻辑。这时你就会在心理抱怨“受够了”。那么我该怎么做才能把这些无聊的逻辑从我的代码中剥离出去,让该处理的人去处理呢?这时你要做的是移交或者委派给相应职能的别人。幸运的是,.NET为此提供了一种叫做“委托”的机制。

三、委托

如果你有C/C++编程背景,描述委托最好的方法是“函数指针”。对所有人来说,可以认为把委托传递给方法与把值或对象传递给方法一样。比如下面三行代码就表现出一样的基本原则:你在传递数据给Process处理而不是你自己使用。

 
// 给方法Process传递一个整形值
Process( 5 );
// 给方法Process传递一个ArrayList的引用
Process( new ArrayList() );
// 给方法Process传递一个方法的引用
Process( discountDelegate );

DiscountDelegate是什么?我如何创建?Process方法如何使用?首先如同声明一个类一样,声明一个委托类型。

delegate int DiscountDelegate();
  这句话的意思是我们有一个叫DiscountDelegate的委托类型,我们可以像使用类,结构体等一样使用它。它不需要数据参数,但返回一个整数值。像类一样,我们必须创建一个它的实例它才有意义。记住,创建一个委托实例实质上是创建一个方法的引用。创建实例时关键是要明白DiscountDelegate没有任何构造器,它有一个隐式的构造函数来构造一个与它相同签名的方法(没有传入参数,返回一个整数)。那你怎么给这个构造函数一个方法呢?.NET向你提供了一个向它名字一样简单的方法,你所做的只是忽略圆括号。
1
DiscountDelegate discount = new DiscountDelegate(class.method);
  在深入之前,先回到开始的例子,整理一个代码。我们会添加一个Calculator类来帮助我们处理折扣逻辑,并给我们的委托提供一些方法。
delegate int DiscountDelegate();
 
class Program {
    static void Main(string[] args) {
        Calculator calc = new Calculator();
        DiscountDelegate discount = null;
        if (DateTime.Now.Hour < 12) {
            discount = new DiscountDelegate(calc.Morning);
        }
        else if (DateTime.Now.Hour < 20) {
            discount = new DiscountDelegate(calc.Afternoon);
        }
        else {
            discount = new DiscountDelegate(calc.Night);
        }
        new ShoppingCart().Process(discount);
    }
class Calculator {
    public int Morning() {
        return 5;
    }
    public int Afternoon() {
        return 10;
    }
    public int Night() {
        return 15;
    }
class ShoppingCart {
    public void Process(DiscountDelegate discount) {
        int magicDiscount = discount();
        // ...
    }
}

正如你所见,在Calculator类中,我们为每个逻辑分支创建了一个方法。在Main方法中,我们创建一个Calculator实例和一个DiscountDelegate实例,并按照我们所期望的把它们整合在一起。

太棒了,我们不用担心Process方法中的逻辑了,我们只需要简单得回调我们定义的委托。记住!我们不关心委托是如何创建的(或什么时间),我们就像调用其他方法一样调用它。如你所见,另一种理解委托的方法是,它延迟执行一个方法。Calculator方法在过去某个时间本选择,但不会执行,直到我们调用discount()的时候。现在看看我们的解决方案,这里仍然存在一些丑陋的代码。在Calculator类中,我们可以用一个不同的方法来返回替代每个有返回值得方法吗?答案是肯定的,让我们把这些乱糟糟的代码合并起来。

 
delegate int DiscountDelegate();
 
class Program {
    static void Main(string[] args) {
        new ShoppingCart().Process(new DiscountDelegate(Calculator.Calculate));
    }
class Calculator {
    public static int Calculate() {
        int discount = 0;
        if (DateTime.Now.Hour < 12) {
            discount = 5;
        }
        else if (DateTime.Now.Hour < 20) {
            discount = 10;
        }
        else {
            discount = 15;
        }
        return discount;
    }
class ShoppingCart {
    public void Process(DiscountDelegate discount) {
        int magicDiscount = discount();
         // ...
    }
}

这样子看起来更好点。你会注意到我们用一个静态的Calculate方法替换了所有原来的方法,在Main方法中也不用费心维护一个指向DiscountDelegate的引用。现在你明白了所有关于委托的东西了吗?在2004年.NET1.1中可以这么说,但是很不幸的是,这种框架自那以后更加成熟了。

四、灯光,镜头,开始 或者我们需要Func!

微软在.NET 2.0中引入了泛型,并提供了一个泛型委托:Action<T>。老实说,我认为它远不够用。后来在.NET 3.5中,它为我们提供了一些我们不想定义的通用委托。他们扩展了Action,并添加了Func,二者唯一区别在于Func型方法有一个返回值而Action型方法没有。

这意味着我们不需要声明自己的DiscountDelegate,可以用Func<int>替代。为说明这些观点是如何工作的,我们来假设管理层又一次改变了我们的逻辑,我们需要提供一些特殊的折扣。很简单,我们将给Calculate方法传入一个bool型值。

现在我们的委托签名变成Func<bool,int>。注意Calculate方法现在包含一个bool型参数,我们用一个bool值调用discount()。

class Program {
    static void Main(string[] args) {
        new ShoppingCart().Process(new Func<bool, int>(Calculator.Calculate));
    }
}
 
class Calculator {
    public static int Calculate(bool special) {
        int discount = 0;
        if (DateTime.Now.Hour < 12) {
            discount = 5;
        }
        else if (DateTime.Now.Hour < 20) {
            discount = 10;
        }
        else if (special) {
            discount = 20;
        }
        else {
            discount = 15;
        }
        return discount;
    }
}
 
class ShoppingCart {
    public void Process(Func<bool,int> discount) {
        int magicDiscount = discount(false);
        int magicDiscount2 = discount(true);
    }
}

好像还算不错,我们又省了一行代码,这样算结束了吗?当然没有,我们甚至能省掉类型判断。只要我们传递的方法有严格签名的委托,.NET允许我们完全忽略掉显式创建Func<bool,int>。

//因为Process期望的方法有一个bool型输入参数和返回一个int值,所以下面这句话是正确的
new ShoppingCart().Process(Calculator.Calculate);

至此,首先通过忽略自定义委托,我们省略了代码;然后排出了明确的创建Func委托。我们能继续压缩代码行吗?到此我们才完成此文的一半,答案显然是“能”。

 五、匿名方法

匿名方法能够让你声明一个方法体而不需要给它指定一个名字。在接下来的场景里,它们以一个“普通的”方法存在;但是在你的代码中没有任何方法显式调用它。匿名方法只能在使用委托的时候创建,事实上,它们通过delegate关键字创建。

class Program {
    static void Main(string[] args)     {
        new ShoppingCart().Process(
            new Func<bool, int>(delegate(bool x) { return x ? 10 : 5; }
        ));
    }
}

正如你所见,我们完全删除了Calculator类的需求。你可以在打括号中添加任何其他方法中的逻辑。如果你在看它如何执行时有困难,那就把delegate(bool x)做为一个方法签名,而不是一个关键字。设想这段代码在一个类里,delegate(bool x){return 5;}是一个完整的合法方法声明(我们确实有一个返回值),恰好delegate是一个保留字,在这里,它让这个方法匿名。

至此,我确信现在你知道这里我们甚至能压缩更多的代码。顺利成章的,我们能忽略显式声明Func委托的需要;.NET让我们使用delegate关键字更方便。

class Program {
    static void Main(string[] args) {
        new ShoppingCart().Process(
          delegate(bool x) { return x ? 10 : 5; }
        );
    }
}

当把.NET方法做为委托参数时或处理时间时,就能看到匿名方法的真正用处。之前,你会为你所关注的所有可能行为创建了一个方法,现在你仅需以内联的方式创建它们,并可以避免污染你的命名空间。

// 创建一个匿名比对方法
custs.Sort(delegate(Customer c1, Customer c2) {
    return Comparer<int>.Default.Compare(c1.ID, c2.ID);
});
 
//  创建一个匿名事件
button1.Click += delegate(object o, EventArgs e) { MessageBox.Show("Click!"); };

六、Lambda 表达式

MSDN中写道:“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。你应当明白“用户创建委托”部分,但什么是“表达式”呢?老实说,表达式和表达式树不在此为讨论范围内。现在我们唯一需要明白的是,表达式是.NET程序运行时表示数据或对象的代码(C#代码)。引用Jon Skeet的话:“表达式树是一种表达逻辑,这样其他的代码可以查询的方法。当一个lambda表达式转换成一个表达式树,编译器不会发出了lambda表达式的白细胞介素,它会发出白细胞介素这将会建立一个表达式树表示相同的逻辑。”

我们需要关注的是Lambda表达式替换匿名方法,和其他的特性。回顾我们最后例子,我们已经在一行代码里压缩了处理整个折扣算法的逻辑。

class Program {
    static void Main(string[] args) {
        new ShoppingCart().Process(
            delegate(bool x) { return x ? 10 : 5; }
        );
    }
}

你相信我们能让这个更短吗?Lambda表达式用'=>'运算符表明什么参数传递给表达式。编译器进一步处理,允许我们忽略类型并自动替我们推断这些类型。如果你有2个或更多个参数,你需要用圆括号:(x,y)=>。如果只有一个,你设置不需要这样:x=>。

static void Main(string[] args) {
    Func<bool, int> del = x => x ? 10 : 5;
    new ShoppingCart().Process(del);
}
// 更短啦...
static void Main(string[] args) {
    new ShoppingCart().Process(x => x ? 10 : 5);
}

就是这样子。x被推断为bool型,并且有返回值,因为Process接收一个Func<bool,int>。如果我们想实现像之前那样的完整代码块,我们只需要加上大括号。

static void Main(string[] args) {
    new ShoppingCart().Process(  x => {
        int discount = 0;
        if (DateTime.Now.Hour < 12) {
            discount = 5;
        }
        else if (DateTime.Now.Hour < 20) {
            discount = 10;
        }
        else if(x)  {
            discount = 20;
        }
        else {
             discount = 15;
        }
        return discount;
    });
}

七、写在最后

使用与不使用大括号有一个重要的不同。当你用时,你创建一个“语句Lambda”,反之,它是"表达Lambda"。语句Lambda能执行多条语句(因此需要大括号),但不能创建表达树。你可能只在使用IQueryable接口是遇到这个问题。下面的例子说明这个问题。

List<string> list = new List<string>();
IQueryable<string> query = list.AsQueryable();
list.Add("one");
list.Add("two");
list.Add("three");
 
string foo = list.First(x => x.EndsWith("o"));
string bar = query.First(x => x.EndsWith("o"));
// foo and bar are now both 'two' as expected
foo = list.First(x => { return x.EndsWith("e"); }); //no error
bar = query.First(x => { return x.EndsWith("e"); }); //error
bar = query.First((Func<string,bool>)(x => { return x.EndsWith("e"); })); //no error

倒数第二行在编译时失败。这是因为IQueryable.First期望得到一个表达式作为参数,然而List<T>.First期望得到一个委托。你可以按照最后一行强制转换Lambda到一个委托(使用First的方法重载)。

这里很难结束讨论,但是我觉得必须停止。Lambda大体上分为两类:一类创建匿名方法和委托;另一类创建表达式。表达式自成一体,并不是.NET开发者的必备知识(无疑在LINQ中已有实现)。

 

        此文为CodeProject上的同名文章<C# Delegates,Anonymous Methods, and Lambda Expressions>

08
8

.NET Framework发展简历

0
归档:2013年8月分类:C#和.NET

.NET Framework1.0版本是在2002年正式发布的,当时获得了世界软件界的极大瞩目,影响深远,比尔盖茨说这是为未来10年的战略做准备,现在看来,微软的这个产品获得了巨大成功。

2005年,微软发布了.NET Framework2.0,这个版本是迄今为止最重要的一个版本,2.0为C#引入了范型和公共语言运行时。.NET 3.0版本是基于2.0的运行时的,引入了WPF和XAML语言,还有一项伟大的技术WCF。.NET3.5则引入了C#3.0,并且支持LINQ语言(一种全新的方式来检索数据)和Lambda表达式。.NET4.0是继2.0之后的一个重要版本,引入了动态语言和并行编程。

.NET Framework4.5是基于4.0版本的更新,主要有下面这些更新:

1、异步变成方式,在C#5.0中,异步编程变得更加简单;

2、引入了Windows Store Apps;

3、增强了数据访问方式,主要是Entity Framework5.0的发布;

4、增强了WPF和江MVC升级到了4.0;

29
11

[转]Emgu cv中的SIFT算法实现

0
归档:2012年11月分类:C#和.NET

SIFT算法大家都比较熟悉,网上的版本很多,刚开始接触时我主要研究的是C++,因为相对于C#,基于OPEN CV C++的SIFT算法资料更多,但是由于想要实现较为理想的界面效果,最终还是放弃了使用C++转而使用C#。

 

C#中SIFT算法主要分为三种:

1)脱离Emgu cv平台,完全手动实现所有SIFT算法函数,这样的程序虽然实现有些困难,但是完全可借助已有的C++程序做更改,而且这样做最大的好处就是对SIFT算法的原理有更深的理解。

2)实现时使用少量Emgu cv函数(例如影像的读取,灰度值获得等),但是大部分工作还是依赖于.net平台自行完成。

3)基本上程序完全借助于Emgu cv提供的接口,核心函数完全由Emgu cv提供。

 

前两类程序资源较多,大家也很容易下载,第三类程序资源相对较少,因此我今天简单为大家介绍第三类算法的实现方法,首先回顾一下SIFT算法计算步骤:

 

1. 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。

2. 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。

3. 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。

4. 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。

使用Emgu cv实现上述步骤非常简单,程序如下:

//确定匹配图像

Bitmap bt1 = new Bitmap(@"C:\Users\GAOXIANG\Desktop\111.jpg");
Bitmap bt2 = new Bitmap(@"C:\Users\GAOXIANG\Desktop\222.jpg");

//将图像转为Emgu cv的处理格式

Image<Gray, byte> img1 = new Image<Gray, byte>(bt1);
Image<Gray, byte> img2 = new Image<Gray, byte>(bt2);

//使用Emgu cv探测图片特征点

SIFTDetector sift = new SIFTDetector();
var f1 = sift.DetectFeatures(img1, null);
var f2 = sift.DetectFeatures(img2, null);

到此已经获得了两张相片的特征点f1,f2,接下来就是将相互匹配的特征点相连:

for (int i = 0; i < f1.Length; i++)
{
double[] dist = new double[f2.Length];
int n = 0;
int index = 0;
for (int j = 0; j < f2.Length; j++)
{

//计算待比较特征点描述子的欧式距离
dist[n++] = diedai(f1[i].Descriptor, f2[j].Descriptor);
}

//排序,获得欧式距离最近点以及次近点
for (int k = 0; k < 2; k++)
{
for (int k1 = k+1; k1 < dist.Length; k1++)
{
if (dist[k] > dist[k1])
{
double temp = dist[k];
dist[k] = dist[k1];
dist[k1] = temp;

//记录最近点在相片2中的序列
if (k == 0)
{ index = k1; }
}
}
}

//若最近点与次近点比值小于阈值0.49,绘制特征点连线
if (dist[0]/dist[1] < 0.49)
{
PointF point1 = f1[i].KeyPoint.Point;
PointF point2 = f2[index].KeyPoint.Point;

Graphics g = this.CreateGraphics();
Pen p1 = new Pen(Color.Blue, 3);
g.DrawLine(p1, point1,  point2);//注意:point1,point2位置与具体控件,图像布局有关,请自行调整
}
}

//描述子距离计算函数

private double diedai(float[] p1, float[] p2)
{
double sub=0;
double sqrt=0;
for (int i = 0; i < p1.Length; i++)
{
sub += Math.Pow(p1[i] - p2[i], 2);
}
sqrt = Math.Sqrt(sub);
return sqrt;
}

via:http://blog.csdn.net/xiang__jiangsu/article/details/8116618

21
5

在类库项目(或者其他非Web项目中)有时候需要HttpContext.Current这个方法,我们发现在类里面添加了引用“using System.Web;”之后还是不行,其实解解这个问题很简单,只需要在该项目的“引用”中添加System.Web这个引用就可以了。
另外值得注意的是,在非Web项目中使用HttpContext.Current.Cache、HttpContext.Current.Session等的时候,最好进行判断HttpContext.Current是否为空:
if (HttpContext.Current != null && HttpContext.Current.Session != null)
{
string test = HttpContext.Current.Session["Session"].ToString();
}
这是因为有些情况下Session或者Cache等会被截断,比如在.ashx文件中,默认情况下就会截断Session。当然也可以通过设置在.ashx文件中使用Session,但是为了安全,最后进行判断。
如果要在.ashx文件中使用Session,那么要先引用“using System.Web.SessionState;”,然后继承接口“IRequiresSessionState”,下面是一个例子:
using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.SessionState;
namespace Lemon.Life.WebData
{

/// 演示“在.ashx中使用Session”

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Xml : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Session["Test"] = "Test";
string test = context.Session["Test"].ToString();
context.Response.Write("Hello World");
}
public bool IsReusable
{
get
{ return false;}
}
}
}

21
5

设置Session永不过期,Session不过期

0
归档:2011年5月分类:C#和.NET

很多时候为了需要,必须使用Session,但是Session过期问题一直困扰很多人,我也是。通过网上的搜索,发现了可以通过两种方式了设置,但是只有一个可以永不过期。
保持Session的方法:
(1)、设Session.timeout=-1,或小于0的数。这种方法肯定是不行的,session计算时间以分钟为单位,必须是大于等于1的整数。
(2)、设Session.timeout=99999。这种同样不行,Session有最大时间限制。
其实,Session最大值为24小时,也就是说你最大可以Session.timeout=1440,1441都是不可以有。
所以想通过设Session.timeout的过期时间让Session永不过期是不可能的。写到Cookies里是比较好的方法,但是Cookie非常的危险,如果在外面的电脑很容易被别人劫持,不安全!
那么有没有一种方法可以保持Session呢,可以使用一种办法,就是在要保持session的页里设隐藏iframe
这个Iframe每隔一段时间刷新一次这个Iframe页面就可以了。

21
5

在开发过程中,常常遇到这样的问题:文件,包括图片和文件上传到服务器,而Web服务器和文件服务器不是同一个,而且不在同一个域里面,那么针对于.NET应该如何处理这样的问题呢?
可能很多高手一下子就知道如何解决,但是我确实是经过了一番努力才弄明白,下面就结束如何使用.NET的Windows模拟身份验证。
1、首先引用两个名称空间

using System.Security.Principal;
using System.Runtime.InteropServices;

2、其次定义好模拟权限的调用方法

region 权限模拟

public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);<summary>/// 验证用户,并生成WindowsIdentity 实例
</summary>private static WindowsIdentity GetIdentity(String userName, String domain, String password)
{
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) == 0)
return null;
else if (DuplicateToken(token, 2, ref tokenDuplicate) == 0)
return null;
else
return new WindowsIdentity(tokenDuplicate);
}
public WindowsImpersonationContext GetContext()
{
WindowsIdentity identity = null;
WindowsImpersonationContext impersonationContext = null;
identity = string.IsNullOrEmpty(user) ? null : GetIdentity(user, null, pwd);
// 使用用户凭证进行用户模拟
impersonationContext = (identity == null) ? null : identity.Impersonate();
return impersonationContext;
}
#endregion

3、在调用身份模拟的函数中使用

WindowsImpersonationContext impersonationContext = Instance.GetContext("identityValues");
//中间是处理函数
// 取消用户模拟
if (impersonationContext != null)
{
impersonationContext.Undo();
impersonationContext = null;
}
21
5

很多公司都使用代理上网,这样当然是有原因的,因为要对公司员工访问网络进行监视,但是这很令开发人员郁闷,因为在程序中访问网络资源是需要自己设置代理认证的,我们公司就是这样的。幸亏不要紧,因为C#提供了针对访问网络资源代理的设置,今天来讲解WebClient代理问题。下面是一个实现的例子:
private static string GetHtml(string url, EncodingType type, bool isByProxy)
{
WebClient webClient = new WebClient();//创建WebClient实例myWebClient
if (isByProxy)
{
//创建代理服务器设置对象的实例
System.Net.WebProxy wp = new System.Net.WebProxy("ISASRV:80");//代理名称很端口,要根据你的情况来设置
//代理服务器需要验证
wp.BypassProxyOnLocal = false;
//用户名密码,要根据你的情况来设置
wp.Credentials = new NetworkCredential("UserName", "PWD", "hold");
//将代理服务器设置对象赋予全局设定
GlobalProxySelection.Select = wp;
//获取或设置用于对向Internet 资源的请求进行身份验证的网络凭据。
webClient.Credentials = CredentialCache.DefaultCredentials;
}
byte[] pagedata = webClient.DownloadData(url);//先获取二进制的资源
}

21
5

学习C#编程语言的几个阶段

0
归档:2010年5月分类:C#和.NET

很多人在自己的简历里都写着精通C#编程语言,可是现实中真的有那么多高手吗?下面是李建忠(《Microsoft .NET框架程序设计》作者)对学习C#语言达到的境界的几个层次:
1. 基本运用C#语法,在各种工具和示例代码的支持下,完成一些基本程序任务
2. 熟练掌握面向对象与组件构造,知其然亦知其所以然,完成一般小规模信息管理类软件项目开发任务
3. 深入理解CLR内核机制,对各种类型与.NET平台机制的优缺点、适用场合、效率有清晰把握,能够完成技术复杂度比较高的项目。
4. 能够游刃有余把握松耦合原则,精准运用各种语言构造,架构大型软件项目。
5. 能够站在计算机抽象、系统和历史发展的高度来理解和把握编程语言
个人觉得自己只能够是出于2到3这个阶段。

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。