• 参数和属性


    参数和属性

    CLR默认所有方法参数都传值。

    • 传递引用类型的对象时,对象引用被传递给方法,这意味着方法能修改对象,而调用者能看到这些修改。

    • 对于值类型的实例,传给方法的是实例的一个副本。

     

    C#用关键字out和ref

    • out表明调用方法前,参数可以不初始化;

    • ref表明调用方法前,参数必须初始化完成;

    • 为值类型使用out和ref,效果等同于以传值的方式传递引用类型。

    public sealed class Program {
        public static void Main() {
            Int32 x; // x is uninitialized.
            GetVal(out x); // x doesn’t have to be initialized.
            Console.WriteLine(x); // Displays "10"
        }
        private static void GetVal(out Int32 v) {
            v = 10; // This method must initialize v.
        }
    }
    public sealed class Program {
        public static void Main() {
            Int32 x = 5; // x is initialized.
            AddVal(ref x); // x must be initialized.
            Console.WriteLine(x); // Displays "15"
        }
        private static void AddVal(ref Int32 v) {
            v += 10; // This method can use the initialized value in v.
        }
    }

    向方法传递可变数量的参数

    为了接收可变数量的参数,方法要像下面这样声明:

    static Int32 Add(params Int32[] values) {
        // NOTE: it is possible to pass the 'values'
        // array to other methods if you want to.
        Int32 sum = 0;
        if (values != null) {
        for (Int32 x = 0; x < values.Length; x++)
            sum += values[x];
        }
        return sum;
    }

    要像下面这样调用该方法:

    public static void Main() {
        // Displays "15"
        Console.WriteLine(Add(new Int32[] { 1, 2, 3, 4, 5 } ));
    }

    上述代码编译没有问题,并可以运行,但是有点丑。我们希望能像下面这样调用:

    public static void Main() {
        // Displays "15"
        Console.WriteLine(Add(1, 2, 3, 4, 5));
    }

    让人兴奋的是这样做也没有问题,因为params关键字的存在,编译器向参数应用了定制特性System.ParamArrayAttribute的一个实例。

    注意:

    • params只能应用于方法签名中的最后一个参数。

    • params参数只能标识一维数组(任意类型)。

     

    参数和返回类型的设计规范

    总之要保证方法有尽量大的灵活性,使用方法的适用范围更大。

    • 声明方法的参数类型时,应尽量指定最弱的类型,宁愿要接口也不要基类;

    // 好 Desired: This method uses a weak parameter type
    public void ManipulateItems<T>(IEnumerable<T> collection) { ... }
    // 不好 Undesired: This method uses a strong parameter type
    public void ManipulateItems<T>(List<T> collection) { ... }
     
    • 对于方法的返回值类型最好声明为强类型(防止受限于特定类型);

      // 好 Desired: This method uses a strong return type
      public FileStream OpenFile() { ... }
      // 不好 Undesired: This method uses a weak return type
      public Stream OpenFile() { ... }
      ​
      // Flexible: This method uses a weaker return type
      public IList<String> GetStringCollection() { ... }
      // Inflexible: This method uses a stronger return type
      public List<String> GetStringCollection() { ... }

      这里是不是搞错了,应该是返回类型最好为弱类型才能更灵活??

     

    属性

    CLR支持两种属性:

    • 无参属性:就是平常说的属性。

    • 有参属性:C#将有参属性称为所引器。

    属性这个概念的出现是为了解决类对象的封装,如果不考虑类的封装,直接访问字段就好了,但是这样做非常不安全。

    我们可以将属性想象为智能字段,它相当于对属性进行了一个包装。属性使得对字段的访问更安全,同时又不像调用方法访问字段那样蹩脚,例如:

    使用时要调用方法:

    e.SetName("Jeffrey Richter"); // Updates the employee's name
    String EmployeeName = e.GetName(); // Retrieves the employee's name
    e.SetAge(48); // Updates the employee's age
    e.SetAge(-5); // Throws ArgumentOutOfRangeException
    Int32 EmployeeAge = e.GetAge(); // Retrieves the employee's age

    下面的类使用了属性,它与前面定义的类功能相同:

     在使用时更加方便:

    e.Name = "Jeffrey Richter"; // "Sets" the employee name
    String EmployeeName = e.Name; // "Gets" the employee's name
    e.Age = 48; // "Sets" the employee's age
    e.Age = -5; // Throws ArgumentOutOfRangeException
    Int32 EmployeeAge = e.Age; // "Gets" the employee's age

    CLR支持静态、实例、抽象、和虚属性。另外,属性可用任意“可访问性”修饰符来标记。而且可以在接口中定义。

    每个属性都有名称和类型(类型不能为void)。

    属性不能重载,即不能定义名称相同、类型不同的两个属性。

    属性的get 和 set 方法不一定要访问支持字段。

    声明属性而不提供 get/set 方法的实现,C#会自动为你声明一个私有字段。这是C#提供的一种简介的语法,称为自动实现的属性(AIP),例如下面的Name属性:

    public sealed class Employee {
        // This property is an automatically implemented property
        public String Name { get; set; }
        
        private Int32 m_Age;
        
        public Int32 Age {
            get { return(m_Age); }
            
            set {
                if (value < 0) // The 'value' keyword always identifies the new value.
                throw new ArgumentOutOfRangeException("value", value.ToString(),
                "The value must be greater than or equal to 0");
                m_Age = value;
            }
        }
    }

    属性不能作为 out 或ref 参数传给方法,而字段可以。因为属性的根本是一个方法。

    属性方法可能花较长时间执行,字段访问则总是立即完成。要线程同步不要使用属性,而要使用方法。

     匿名类型:

    利用C#的匿名类型功能,可以用很简洁的语法来自动声明不可变的元组类型。

    // Define a type, construct an instance of it, & initialize its properties
    var o1 = new { Name = "Jeff", Year = 1964 };
    // Display the properties on the console:
    Console.WriteLine("Name={0}, Year={1}", o1.Name, o1.Year);// Displays: Name=Jeff, Year=1964

    编译器会推断每个表达式的类型,创建推断类型的私有字段,为每个字段创建公共只读属性,并创建一个构造器来接受所有这些表达式。在构造器的代码中,会用传给它的表达式的求值结果来初始化私有只读字段。最终编译器生成的类看起来像这样:

    [CompilerGenerated]
    internal sealed class <>f__AnonymousType0<...>: Object {
        private readonly t1 f1;
        public t1 p1 { get { return f1; } }
        ...
        private readonly tn fn;
        public tn pn { get { return fn; } }
        public <>f__AnonymousType0<...>(t1 a1, ..., tn an) {
        f1 = a1; ...; fn = an; // Set all fields
        }
        public override Boolean Equals(Object value) {
        // Return false if any fields don't match; else true
        }
        public override Int32 GetHashCode() {
        // Returns a hash code generated from each fields' hash code
        }
        public override String ToString() {
        // Return comma•separated set of property name = value pairs
        }
    }
    • 编译器会生成Equals 和 GetHashCode方法,因此匿名类型的实例能放到哈希表集合中。

    • 属性是只读的,而不是可读可写,目的是防止对象的哈希码发生改变。

    • 如果你在源代码中定义了多个匿名类型,而且这些类型具有相同的结构,那么它只会创建一个匿名类型定义,但创建该类型的多个实例。

    • 由于这种类型的同一性,所以可以创建一个隐式类型的数组,在其中包含一组匿名类型的对象。

    // This works because all of the objects are of the same anonymous type
    var people = new[] {
        o1, // From earlier in this section
        new { Name = "Kristin", Year = 1970 },
        new { Name = "Aidan", Year = 2003 },
        new { Name = "Grant", Year = 2008 }
    };

    匿名类型经常用于LINQ 的使用中。用LINQ 执行查询,从而生成一组对象构成的集合,这些对象都是相同的匿名类型。

    String myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    var query =
                from pathname in Directory.GetFiles(myDocuments)
                let LastWriteTime = File.GetLastWriteTime(pathname)
                where LastWriteTime > (DateTime.Now • TimeSpan.FromDays(7))
                orderby LastWriteTime
                select new { Path = pathname, LastWriteTime };// Set of anonymous type objects
    foreach (var file in query)
        Console.WriteLine("LastWriteTime={0}, Path={1}", file.LastWriteTime, file.Path);

    注意: 匿名类型的实例不能泄漏到方法外部。方法原型不能接受匿名类型的参数,因为无法指定匿名类型。类似地,方法也不能返回对匿名类型的引用。

     

    System.Tuple 类型

    和匿名类型相似,Tuple 创建好后就不可变了(所有属性都只读)。虽然在Tuple 类型定义中看不到其他的,但Tuple 类还提供了 CompareTo,Equals,GetHashCode 和ToString 方法以及Size 属性。

    所有Tuple 类型都实现了IStructuralEquatable,IStructuralComparable 和 IComparable 接口,所以可以比较两个 Tuple对象,对它们的字段进行比较。

    // Returns minimum in Item1 & maximum in Item2
    private static Tuple<Int32, Int32> MinMax(Int32 a, Int32 b) {
        return new Tuple<Int32, Int32>(Math.Min(a, b), Math.Max(a, b));
    }
    ​
    // This shows how to call the method and how to use the returned Tuple
    private static void TupleTypes() {
        var minmax = MinMax(6, 2);
        Console.WriteLine("Min={0}, Max={1}", minmax.Item1, minmax.Item2); // Min=2, Max=6
    }

    注意:Tuple 的生产者和消费者必须对 Item 属性返回的内容又一个清楚的理解。对于 Tuple 类型,属性一律被 Microsoft 称为 Item,编程者改不了。使用时要在自己的代码中添加详细说明每个Item 代表什么。

     

    有参属性

    无参属性用起来就像是在访问字段。有参属性它的 Get 访问器方法接受一个或多个参数,Set 访问器方法接受两个或多个参数。C# 称有参属性为所引器。

    C# 使用数组风格的语法来公开有参属性(索引器)。换句话说,可将所引器看成是C# 开发人员对 “ [ ] ” 操作符的重载。下面是一个示例 BitArray 类,它允许数组风格的语法来索引由该类的实例维护的一组二进制位。

    using System;
    public sealed class BitArray
    {
        // Private array of bytes that hold the bits
        private Byte[] m_byteArray;
        private Int32 m_numBits;
        // Constructor that allocates the byte array and sets all bits to 0
        public BitArray(Int32 numBits)
        {
            // Validate arguments first.
            if (numBits <= 0)
                throw new ArgumentOutOfRangeException("numBits must be > 0");
            // Save the number of bits.
            m_numBits = numBits;
            // Allocate the bytes for the bit array.
            m_byteArray = new Byte[(numBits + 7) / 8];
        }
        // This is the indexer (parameterful property).
        public Boolean this[Int32 bitPos]
        {
            // This is the indexer's get accessor method.
            get
            {
                // Validate arguments first
                if ((bitPos < 0) || (bitPos >= m_numBits))
                    throw new ArgumentOutOfRangeException("bitPos");
                // Return the state of the indexed bit.
                return (m_byteArray[bitPos / 8] & (1 << (bitPos % 8))) != 0;
            }
            // This is the indexer's set accessor method.
            set
            {
                if ((bitPos < 0) || (bitPos >= m_numBits))
                    throw new ArgumentOutOfRangeException("bitPos", bitPos.ToString());
                if (value)
                {
                    // Turn the indexed bit on.
                    m_byteArray[bitPos / 8] = (Byte)
                    (m_byteArray[bitPos / 8] | (1 << (bitPos % 8)));
                }
                else
                {
                    // Turn the indexed bit off.
                    m_byteArray[bitPos / 8] = (Byte)
                    (m_byteArray[bitPos / 8] & ~(1 << (bitPos % 8)));
                }
            }
        }
    }

    索引器用起来很简单:

    // Allocate a BitArray that can hold 14 bits.
    BitArray ba = new BitArray(14);
    // Turn all the even•numbered bits on by calling the set accessor.
    for (Int32 x = 0; x< 14; x++) {
        ba[x] = (x % 2 == 0);
    }
    // Show the state of all the bits by calling the get accessor.
    for (Int32 x = 0; x< 14; x++) {
        Console.WriteLine("Bit " + x + " is " + (ba[x]? "On" : "Off"));
    }

    System.Collections.Generic.Dictionary 中就提供了类似的索引器,他获取一个键,并返回该键关联的值。

    和无参属性不同,类型可提供多个重载的索引器。

    C# 不支持定义静态索引器属性,只允许在对象的实例上定义索引器。可以使用this[...] 作为表达索引器的语法。

    属性本质上是方法,而 c# 和CLR 允许泛型方法,但C# 不允许属性有自己的泛型类型参数。

  • 相关阅读:
    一天搞懂深度学习--深度学习简介
    Ubuntu16.04下安装Hive
    Ubuntu16.04下安装Hadoop
    Hive入门学习--HIve简介
    循环神经网络(RNN)--学习笔记
    如何使用GitHub
    python pandas import 失败
    Azure ARM VM内部关机了,但门户却显示虚拟机还在处在“正在运行”的状态
    Exchange 2010 与 RMS(集成权限管理服务)集成
    统计 Exchange 2010 时间段收发邮件总量
  • 原文地址:https://www.cnblogs.com/mingjie-c/p/11650359.html
Copyright © 2020-2023  润新知