C#积累(一)——扩展方法就近原则和匿名类型的成员探讨

C#3.0多了扩展方法和匿名类型,今天探讨下这两个知识点一些容易忽视的地方

项目:C# 控制台应用程序

版本:.net 3.5

运行IDE:VS 2008 SP1

项目文件:Ext.cs、Anonymous.cs、Program.cs

一,扩展方法就近原则

首先要注意扩展方法可以扩展:类、接口、结构体

其次扩展方法有就近原则,也就是如果在你的程序里有两个一模一样的扩展方法,一个和你的使用类是处于同一命名空间里,另外一个处于别的命名空间里,这个时候会优先使用同一命名空间里的扩展方法,也就是说“血缘关系”越近,越被青睐(此句为转摘)。

那么在同一命名空间下在有同名扩展方法时是否会有就近原则呢?答案是No,会报错,请看:

c#类库文件:Ext.cs

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExtAndVar
{
    
public class Ext
    {

    }

    
static class ExtClassOne
    {
        
public static void ExtOutPut(this Ext ext,string value)
        {
            Console.WriteLine(value);
        }
    }

    
/*static class ExtClassTwo//如果这个类存在,调用ext.ExtOutPut("");会出现编译错误:在以下方法或属性之间的调用不明确:“ExtAndVar.ExtClassOne.ExtOutPut(ExtAndVar.Ext, string)”和“ExtAndVar.ExtClassTwo.ExtOutPut(ExtAndVar.Ext, string)”
    {
        public static void ExtOutPut(this Ext ext, string value)
        {
            Console.WriteLine(value);
        }
    }
*/
}

另外有个容易被忽视的知识点:扩展方法实现为静态方法,并使其至少具有与包含类相同的可见性。

如果像下面这么写会报错:可访问性不一致: 参数类型“ExtAndVar.Ext”比方法“ExtAndVar.ExtClassOne.ExtOutPut(ExtAndVar.Ext, string)”的可访问性低

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExtAndVar
{
    
class Ext
    { 

    }

    
public static class ExtClassOne
    {
        
public static void ExtOutPut(this Ext ext, string value)
        {
            Console.WriteLine(value);
        }
    }

    
/*static class ExtClassTwo//如果这个类存在,调用ext.ExtOutPut("");会出现编译错误:在以下方法或属性之间的调用不明确:“ExtAndVar.ExtClassOne.ExtOutPut(ExtAndVar.Ext, string)”和“ExtAndVar.ExtClassTwo.ExtOutPut(ExtAndVar.Ext, string)”
    {
        public static void ExtOutPut(this Ext ext, string value)
        {
            Console.WriteLine(value);
        }
    }
*/
}

 言下之意就是被扩展类Ext的访问性不能比ExtClassOne低也不能比ExtOutPut低,这么做是为了保证能访问到ExtClassOne.ExtOutPut的地方也能访问到Ext

二、匿名类型成员允许的类型

匿名类型的成员都是公共只读属性,所以只要属性支持的成员就可以作为匿名类型的成员

以下是MSDN的原话:匿名类型是由一个或多个公共只读属性组成的类型。不允许包含其他种类的类成员(如方法或显式事件)。

下面这个例子我总结了下匿名类型支持的成员,以及隐式类型var的一些问题:

c#类库文件:Anonymous.cs

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExtAndVar
{
    
class Anonymous
    {
        
public delegate void TestHandler();
        
public static event TestHandler et;
        
public static event TestHandler ett
        {
            add
            {

            }
            remove
            {

            }
        }

        
public static void Handler()
        {
            Console.WriteLine(
"Call Handler!");
        }

        
public static void TestOne()
        {
            var t 
= new StringBuilder();
            t.Append(
"看到没?var引用完全可以调用被引用对象的成员");

            
object obj = new StringBuilder();
            
//obj是object的引用,只能调用object表现出来的成员

            Console.WriteLine(t 
is object);//输出True,因为t引用StringBuilder,StringBuilder继承object
        }

        
public static void TestTwo()
        {
            var t 
= new {i=3};
            
//t.i = 4;这句会编译错误,记住匿名类型内的成员都是公共只读属性!

            var r 
= new {s=new StringBuilder() };//匿名类型的成员可以是任何C#属性支持的成员
            r.s.Append("");
        }

        
public static void TestThree()
        {
            TestHandler th 
= Handler;
            var t 
= new {th};//这表明匿名类型成员可以是委托实例
            t.th();
        }

        
public static void TestFour()
        {
            et 
+= Handler;
            var t 
= new { et };//这表明匿名类型成员可以是隐式声明的事件(ps:因为隐式声明的事件就是一个委托实例)
            t.et();

            ett 
+= Handler;
            
//var r = new { ett };//编译出错,匿名类型的成员不能是显式声明的事件(ps:因为显式声明的事件是一种特殊的C#属性,匿名类型成员不能是属性)
        }

        
public static void TestFive()
        {
            var al 
= new[] { new { a = 1 }, new { a = 2 }, new { a = 3 } };//请注意匿名类型数组在初始化的时候不能指定数组长度,例如new[3] { new { a = 1 }, new { a = 2 }, new { a = 3 } }编译会出错,因为指定了长度3
            
//var bl = new[] { new { a = 1 }, new { a = 2 }, new { b = 3 } };//编译出错,说明匿名类型数组里面装的如果也是匿名类型,要求所装的匿名类型是同一个类型(即匿名类型结构相同)
            int count = al.Length;
        }

        
public static void TestSix()
        {
            var t 
= new {i=3 };
            
//var r = new {int i=3 };//编译出错,匿名类型成员不需要指定类型,匿名类型成员的类型会从赋值中推断出来
        }

        
public static object TestSeven()
        {
            var t 
= 1;
            
return t;//可以用object返回var类型
        }

        
public static int TestEight()
        {
            var t 
= 1;
            
return t;//也可以返回var实际引用的类型
        }
    }
}

最后是一个CLR启动文件,用于调用上面两个测试:

Program.cs:

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExtAndVar
{
    
class Program
    {

        
static void ExtInvoke()
        {
            Console.WriteLine(
"------Ext------");
            Ext ext 
= new Ext();
            ext.ExtOutPut(
"Test");

            Console.WriteLine(
"\n");
        }

        
static void AnonymousInvoke()
        {
            Console.WriteLine(
"------Anonymous------");
            Anonymous.TestOne();
            Anonymous.TestThree();
            Anonymous.TestFour();

            Console.WriteLine(Anonymous.TestSeven());
            Console.WriteLine(Anonymous.TestEight());
        }

        
static void Main(string[] args)
        {
            ExtInvoke();
            AnonymousInvoke();
        }
    }
}
原文地址:https://www.cnblogs.com/OpenCoder/p/1642693.html