RSS 2.0 Feed
2007-06 Entries
摘要:默认情况下,首次运行 ClickOnce 应用程序时,会下载该应用程序中包含的所有程序集。但是一些特殊的场景我们可能不希望它这么干,而是希望按照一定规测或者需要用到某个组件的时候再下载。 比如我们可能会有下面一些应用场景的需求: 1、我开发的这个客户端程序是要收费的。但是免费用户也可以使用部分功能。我在技术实现上把收费用户使用的功能封装到了A.dll 组件了,我希望免费用户根本无法获得A.dll,只有收费用户才能获得A.dll,并加载A.dll中的收费功能。 2、我整个应用程序非常大,我不希望我每次更新,用户都需要把所有应用程序都下载下来,用户应该只需要下载他用到的功能组件。   下面我们就来实现一个简单的按需下载的例子,我们完全可以在这个例子基础上,实现上述提到的应用场景1。 演示步骤: 一、新建一个 类库 Project 定义类库输出应用程序名为:OnDemandAssembly,即这个类库编译后产生的文件名为 OnDemandAssembly.dll 这个类库中有如下代码,我们用这段代码来模拟上面提到场景中的一些需要按需加载的功能或者是收费的功能,我们将在主程序中调用这个功能: using System; namespace OnDemandAssembly{    public class DynamicClass    {        public string Message        {            get            {                return "郭红俊测试ClickOnce按需加载功能。";            }        }     }}   二、新建一个 Window 应用程序 我们将在这个Window 应用程序中加载上述类库。   为了便于我们演示这个程序,请确保这个Window程序具备以下功能: 1、有一个 textbox 控件,这个控件在 Window 的OnLoad 事件中,把应用程序的目录显示在这里,方便我们去监控是否上述 OnDemandAssembly.dll 组件被加载了。 代码如下: private void Form1_Load(object sender, EventArgs e){    this.textBox1.Text = Application.StartupPath;} 2、由一个  Button  按钮,点击这个按钮后,我们把 OnDemandAssembly.dll 组件中的 Message 信息显示出来,代码如下: private void button1_Click(object sender, EventArgs e){    DynamicClass o = new DynamicClass();    MessageBox.Show(o.Message);}   Window程序关于ClickOnce设置比较特殊的地方: 我们来配置ClickOnce发布的一些特殊参数,确保可以按需下载需要的组件 本文中没有详细描述ClickOnce设置的各个步骤和参数,如果你对ClickOnce不是很熟悉,建议你首先看一些ClickOnce的入门文章再来看本文。 比如:http://blog.oracle.com.cn/155011/viewspace_3603.html 这里提供的ClickOnce 文章(这里是提供了一个word压缩文件下载,文章在压缩的Word文件中)   选择我们的Window程序,在右键菜单中选择属性,在属性页中选择发布(Publish)标签页。 然后点击 Application File 按钮,我们来设置,需要发布的文件。如下图:  ......[阅读全文]

posted @ | Feedback (10) | Filed Under [ .net 编程心得 VS2005相关 ]

摘要:扩展方法是一个双刃剑,你如果要用扩展方法,就一定得准备碰到别人也用扩展方法,并且万一出现你们都对同一个类进行扩展函数名,参数一样的扩展时候, 会出的各种问题.   说明:以下测试是基于Orcas Beta1环境的测试,VS2008 正式版发布后,也许会发生变化。   如果你对扩展方法不是很熟悉,建议先看我之前的两篇博客: C#3.0 中的扩展方法 (Extension Methods) C#3.0 中使用扩展方法来扩展接口   如果我们项目中,在不经意期间,出现了重名的扩展方法,这时候编译器会如何处理呢?这就是本文要探讨的问题。   先说结论:编译器按照下述三个优先级顺序去寻找到底哪个方法被调用到: 假设,我们是在A 命名空间下,调用 类 B 的 C 方法。 类的 C 方法可能是在 D 命名空间下使用扩展方法扩展实现的。   1、类 B 中是否真实存在 C 方法(非扩展方法),如果存在,则调用之,存在时,不考虑是否有扩展,直接进行调用。(这种情况请看我之前的博客:C#3.0 中的扩展方法 (Extension Methods)) 2、命名空间 A ,是否存在对C方法的扩展,如果存在,则调用这种情况下的扩展;看示例代码二 3、调用代码中,除了 命名空间 A,还引用了其他命名空间 D和 F。A命名空间没有对C方法进行扩展,则调用D命名空间下的扩展。   特殊情况: 如果 F 和 D 命名空间都对 C 方法进行了扩展,则编译器报错误;看下面的示例代码一 如果 D 和 A命名空间都对 C 方法进行了扩展,则调用 A命名空间下的扩展,编译器不会报错;看示例代码二   示例代码一: 以下代码会编译时报错: The call is ambiguous between the following methods or properties: 'ConsoleApplication1.MyClass01.DO(int)' and 'ConsoleApplication2.MyClass02.DO(int) 表明     using ConsoleApplication2;     using ConsoleApplication1;  导致 ConsoleApplication2 和 ConsoleApplication1  命名空间同级,同级搜索出现重名,报错。 namespace ConsoleApplication2{    using System;    static class MyClass02    {        internal static void DO(this int i)        {            Console.WriteLine(string.Format("ConsoleApplication2.MyClass02.DO {0}", i));        }    }} namespace ConsoleApplication1{    using System;    static class MyClass01    {        public static void DO(this int i)        {            Console.WriteLine(string.Format("HongJunGuo.MyClass01.DO {0}", i));        }   ......[阅读全文]

posted @ | Feedback (12) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:DLINQ和XLINQ的具体查询,更新等操作不是本文讨论重点,本文重点解决如何获取需要查询的数据集。   DLINQ 如何链接到数据库? DLINQ可以访问DataSet,这种情况我们在本文就不考虑了, 本文考虑的是直接用DLINQ访问数据库,我们如果用VS Orcas中的LINQ到SQL的新ORM设计器,VS替我们产生了一些代码,这种情况也不是本文考虑的范围。 本文我们要考虑的事情是:如何自己编码去链接数据库,这样我们才能对DLINQ链接数据库有更深入的了解。   下面是一个简单的DLINQ代码:获得 pubs 数据库 authors 表的所有作者的 au_id 信息。 using System;using System.Linq;using System.Data.Linq;  // 这个命名空间在单独的组件 System.Data.Linq.dll 中 public class DLinqTest{    public static void DoSomeThing()    {        // 链接字符串                string connectionString = "Data Source=192.168.5.2;Initial Catalog=pubs;Persist Security Info=True;User ID=sa;Password=******";         // 我们就是通过使用 DataContext  来 DLINQ链接数据库的。        DataContext db = new DataContext(connectionString);        Table<Authors> authors = db.GetTable<Authors>();        var users = from a in authors orderby a.au_id select a;        foreach (var a in users)        {            Console.WriteLine(a.au_id);        }    }} // 数据库中的表结构影射的实体对象,注意其中的 Attribute. [Table(Name = "authors")]public class Authors{    [Column(IsPrimaryKey = true)]    public string au_id { get; set; }    [Column]    public string au_lname {......[阅读全文]

posted @ | Feedback (6) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:Lambda 表达式(拉姆达表达式) 和 匿名方法 其实是一件事情。唯一的不同是:他们语法表现形式不同。Lambda 表达式是在语法方面的更进一步的进化。在本质上,他们是一件事情。他们的作用都是:产生方法。即:内联方法。 引用自 C#首席架构师Anders Hejlsberg 的原话: http://www.ondotnet.com/pub/a/dotnet/2005/10/31/interview-with-anders-hejlsberg-part-2.html?page=2  lambda expressions and anonymous methods are really just two words for the same thing. The only thing that differs is, "What does the syntax look like?" And the lambda expressions are a further evolution of the syntax.But underneath, they do the same thing. They generate methods. You know, they're in-line methods. 所以:我们要了解 Lambda 表达式 就应该同时也了解 匿名方法。下面先看一个简单的代码例子,分别用匿名方法和Lambda 表达式来实现对数组的搜索:   使用 .net 2.0 的匿名方法来搜索字符串数组中包含 a 的字符串数组 static void Main(string[] args){    string[] list = new string[] { "abc", "12", "java" };    string[] ll = Array.FindAll(list,        delegate(string s)        {            return s.IndexOf("a") >= 0;        }        );    foreach (string var in ll)   ......[阅读全文]

posted @ | Feedback (9) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:首先来看一个很简单的LINQ查询例子,查询一个int 数组中小于5的数字,并按照大小顺序排列: class Program{    static void Main(string[] args)    {        int[] arr = new int[] { 8, 5, 89, 3, 56, 4, 1, 58 };         var m = from n in arr where n < 5 orderby n select n;        foreach (var n in m)        {            Console.WriteLine(n);        }        Console.ReadLine();    }} 上述代码除了LINQ查询语法外,其他都是我们所熟悉的语法,而LINQ查询语法跟SQL查询语法很相识,除了先后顺序。 Q:为何 LINQ 查询语法是以 from 关键字开头的,而不是以 select 关键字开头的?select 开头这种写法跟SQL的写法更接近,更易懂呀? A:简单来说,为了IDE的智能感知(Intelisence)这个功能,select 关键字放在后面了。 编程语言以 select 开头写LINQ查询语法不是没出现过,你如果使用过2005年的VB9 CTP 版本,那时候VB9的LINQ查询语法就是 select 关键字在前面,但是 select 关键字在前面,在做智能感知(Intelisence)时候就很头大。经过微软IDE组的权衡,确定了把 from 关键字放在最前面。 比如:你看 http://blog.joycode.com/saucer/archive/2005/09/16/63513.aspx 这篇博客,那时候 VB9 LINQ的查询语法还是 select 参数在最前面。不过后来 VB9 测试版改成了跟 C# 一样的做法, from 关键字放在最前面了。 更详细的解释,来自装配脑袋 假设你要书写这样的代码:Select p.Name, p.Age From p In persons Where xxx ,代码是一个个字符输入的。 我们在写到 p in persons 之前,p 的类型是无法推测的,所以写 Select p. 的时候,Name之类的属性不会弹出智能提示来。 这样就需要先去写 From 这句,再回来写......[阅读全文]

posted @ | Feedback (9) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:C# 3.0 中对象初始化器(Object Initializers)  和 集合初始化器(Collection Initializers) ,就是简化我们的代码,让本来几行才能写完的代码一行写完。这样在LINQ的使用中,我们才不会把一个LINQ表达式写的巨复杂无比。   由于我看到几篇讲 对象初始化器(Object Initializers)和集合初始化器(Collection Initializers) 的文章,都是一个简单的例子,一些稍稍特殊一点的场景的初始化赋值并没有涉及,所以我特整理这篇博客。   关于对象初始化器(Object Initializers) 的一些问题:   问题一: 对象初始化器允许只给部分值赋值么?即不给其中一些值赋值 答案:允许;参考后面的代码。 问题二:对象初始化器允许给internal 的成员赋值?(私有成员肯定不用想了,肯定不能赋值。) 答案:允许;参考下面的代码。   public class Person{    public string FirstName { get; set; }    public string LastName { get; set; }    public int Age { get; set; }    private int test01 = 25;    internal int test02;} class Program{    static void Main(string[] args)    {        Person person = new Person { FirstName = "Scott", LastName = "Guthrie", test02 = 56, };        Console.WriteLine(person.test02);        Console.WriteLine(person.Age);        Console.ReadLine();    } }   问题三:对象初始化器是否可以结合构造函数一起使用? 答案:可以参看如下代码就可以正常使用: var......[阅读全文]

posted @ | Feedback (5) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:有关扩展方法的一些知识点请参看我的前篇博客:C#3.0 中的扩展方法 (Extension Methods) 前篇博客中我重点介绍了两个特殊场景: 1、扩展方法跟原来类的方法重名时候的处理逻辑 2、扩展方法的嵌套 现在我们来看扩展方法使用的第三个场景:使用扩展方法来扩展接口   应用场景举例: 我们有时候会发现最初定义的一个接口,在现在环境下,这个接口需要再增加一个函数。有了扩展方法后,我们在这种情况下又多了一种实现的选择。下面以代码来说明这个问题: namespace Hongjun.Guo{    interface MyInterface    {        void Test01();    }     public class MyClass : MyInterface    {        public void Test01()        {            Console.WriteLine("Test01");        }    }     static class MyExtensionMethods    {        public static void MyTest002(this MyInterface ii)        {            ii.Test01();        }     } }   调用方法: using Hongjun.Guo; static void Main(string[] args){    MyInterface mm = new MyClass();    mm.MyTest002();    Console.ReadLine();}   分析这样做的好处: 1、如果我们实现 MyInterface 接口的类很多,这些类没有派生关系,这时候我们要在接口上新增一个函数,按照以前的做法,这个接口的实现类有多少个,我们就需要改多少个,使用了扩展方法后,我们只需要改一个地方。减少代码量。如果实现这个接口的类被封装在不同的组件中,一些组件由于其他原因难以修改,这时候用扩展方法来扩展接口真是给我们一剂良药呀; 2、扩展方法被调用到,前提条件是扩展方法所在的命名空间被使用了。我们如果把接口跟扩展方法放到同一个命名空间,扩展方法需要引用命名空间的问题就可以认为不存在了。因为你要用这个接口,必然会引用这个命名空间。 3、扩展方法扩展的接口,与你自己实现这个接口的类中自己实现的函数重名时候的问题(这时候也可以简单认为是这个类 override 了这个函数实现)。 先说结论:这两个不存在冲突问题,你如果是接口调用,则是扩展方法,如果是实现类调用,则是实现类自己的方法,参看我前面博客对扩展方法跟原来类的方法重名时候的处理逻辑的描述,下面再以一段代码来描述这个问题。 namespace Hongjun.Guo{    interface MyInterface    {        void Test01();    }     static class MyExtensionMethods    {        public static......[阅读全文]

posted @ | Feedback (8) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:今天早上在MSDN站点看到这样一篇文章: C# 3.0 LINQ 的演变及其对 C# 设计的影响 。 从这篇文章我们就可以明显的看到,C# 3.0 所新增的这些特性,可以认为都是为了LINQ 的发展,为了LINQ更易用,所创建的一些新特性,当然这些新特性也可以被非LINQ相关功能所使用。LINQ的发展,带动了C#3.0 这一系列新特性的产生。   回来说我们的话题,扩展方法,首先看一个最简单的代码例子:   扩展方法的定义,需要注意三个部分:1、静态类(私有公共都可以);2、静态方法(私有公共都可以);3、第一个函数参数前带 this(必须是第一参数前) namespace Hongjun.Guo{    static class MyExtensionMethods    {        internal static void Print(this object s)        {            Console.WriteLine(s);        }    }} 扩展方法的使用,需要注意点: using 你定义所在的命名空间。 using Hongjun.Guo; static void Main(string[] args){    object o = "dsdgs";    o.Print();} 这样一个简简单单的代码后,我们就可以很方便的对一些不开源的第三方控件增加很多我们自己额外需要的功能。    扩展方法的使用,有时候并不是这么简简单单,我们来看一些特殊情况,通过这些特殊情况的分析,我们可以更深入的了解扩展方法:   情况一 : 扩展方法跟原来类的方法重名时候的处理逻辑。 场景:我们是对一个第三方的没有开放源代码组件的一个类扩展了一个方法,比如方法: Print 。过了一段时间后,这个第三方的组件发布了新版本,该类的增加了 Print 方法。这时候会出何种效果?? 问题演示代码: namespace Hongjun.Guo{     public class MyClass    {        public void Print()        {            Console.WriteLine("****");        }    }     static class MyExtensionMethods    {        internal static void Print(this MyClass s)        {            Console.WriteLine("haha " +......[阅读全文]

posted @ | Feedback (16) | Filed Under [ .net 编程心得 .net 3.5 ]

摘要:使用自动属性后,你可以不用手工声明一个私有成员变量以及编写get/set逻辑,取而代之的是,编译器会自动为你生成一个私有变量和默认的get/set 操作。系统为你产生的私有变量在IDE中,你是看不到的,如下图: 当然如果你希望属性中有些赋值或者取值逻辑校验,自动属性可是不适合你的。   上面的代码我们编译后,再用 Reflector 反编译后,我们可以看到上述代码中的属性变成了如下代码:这个编译器给我们产生的私有变量,显然不是那么容易重名的。 [CompilerGenerated]private string <>k__AutomaticallyGeneratedPropertyField0; public string Name{    [CompilerGenerated]    get    {        return this.<>k__AutomaticallyGeneratedPropertyField0;     }    [CompilerGenerated]    set    {        this.<>k__AutomaticallyGeneratedPropertyField0 = value;    }}   注意:如果你只希望属性有 get 或者 set 方法,这些情况都是无法使用 自动属性的,需要你自己来书写。否则就会报如下的错误: 'ConsoleApplication1.MyClass.Name.set' must declare a body because it is not marked abstract or extern. Automatically implemented properties must define both get and set accessors.      但是 get 和 set 访问级别不一样,比如一个是 public,一个是 internal,则可以书写成下述方式,而不会报错误。 public int ID { get; internal set; }   参考资料: Orcas中C#语言的新特性:自动属性,对象初始化器,和集合初始化器...[阅读全文]

posted @ | Feedback (5) | Filed Under [ .net 编程心得 技术随笔 读书笔记 .net 3.5 ]