首页 > 代码库 > C#编程笔记一

C#编程笔记一

---恢复内容开始---

编程笔记

 

override与new实现的版本控制

// versioning.cs
// CS0114 expected
public class MyBase 
{
   public virtual string Meth1() 
   {
      return "MyBase-Meth1";
   }
   public virtual string Meth2() 
   {
      return "MyBase-Meth2";
   }
   public virtual string Meth3() 
   {
      return "MyBase-Meth3";
   }
}

class MyDerived : MyBase 
{
   // Overrides the virtual method Meth1 using the override keyword:
   public override string Meth1() 
   {
      return "MyDerived-Meth1";
   }
   // Explicitly hide the virtual method Meth2 using the new
   // keyword:
   public new string Meth2() 
   {
      return "MyDerived-Meth2";
   }
   // Because no keyword is specified in the following declaration
   // a warning will be issued to alert the programmer that 
   // the method hides the inherited member MyBase.Meth3():
   public string Meth3() 
   {
      return "MyDerived-Meth3";
   }

   public static void Main() 
   {
      MyDerived mD = new MyDerived();
      MyBase mB = (MyBase) mD;

      System.Console.WriteLine(mB.Meth1());
      System.Console.WriteLine(mB.Meth2());
      System.Console.WriteLine(mB.Meth3());
   }
}

 

 

问题:将借口的类型作为返回对象是什么意思。。。

 

问题在下一个章节的解答中。

就是迭代器的实现,用来和foreach来匹配

另外Unity里的协程用到的也是迭代器的方法。

上个示例代码

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

class testClass : MonoBehaviour
{

    void Start()
    {
        print("?");
        TestEnum _t = new TestEnum(10);
        foreach (string item in _t)
        {
            print(item);
        }
        
    }
}

class TestEnum : IEnumerable
{
    /// <summary>
    /// 生成n组n位随机数序列
    /// </summary>
   public TestEnum(int num)
    {
        _Id = new string[num];
        for(int i = 0; i < num; i++)
        {
            _Id[i] = UnityEngine.Random.Range(0, 10000f).ToString();
        }
    }

    string[] _Id;
    public IEnumerator GetEnumerator()
    {
       return new TestEnumtor(this);
    }
    class TestEnumtor : IEnumerator
    {
        private TestEnum testEnum;
        private int index = 0;

        public TestEnumtor(TestEnum testEnum)
        {
            this.testEnum = testEnum;
        }

        public object Current
        {
            get
            {
                return testEnum._Id[index];
            }
        }

        public bool MoveNext()
        {
            if (index < testEnum._Id.Length-1)
            {
                index++;
                return true;
            }
            else
            {
                return false;
            }
        }

        public void Reset()
        {
            index = -1;
        }
    }
}

关于结构和类:

起码的一点,结构是值类型,类对象是引用类型。

同样的,传参时结构是按值传递,而类是按引用传递。

结构不能定义默认构造函数

使用 New 运算符创建结构对象时,将创建该结构对象,并且调用适当的构造函数。与类不同的是,结构的实例化可以不使用 New 运算符。如果不使用“新建”(new),那么在初始化所有字段之前,字段将保持未赋值状态,且对象不可用。

 

对于结构,不像类那样存在继承。一个结构不能从另一个结构或类继承,而且不能作为一个类的基。但是,结构从基类对象继承。结构可实现接口,而且实现方式与类实现接口的方式完全相同。以下是结构实现接口的代码片段:

 

索引器的用法

就是能用索引器的方式来返回类里的属性,具体在VS里找到indexer这个提示符就可以补全出这个结构来了,不做赘述.

但是需要注意一点,这个方法最好是get和set属性都使用。一旦只使用set属性,还不如直接用方法来实现,不要用索引器

 

类型转换

隐式转换implicit和显式转换explicit,也比较好理解。语法上也是结合了运算符重载。

定义了同类型的显示转换/隐式转换就不需要定义另外一种了。

另外记住要声明为static。

直接上个例子就行了

   static public implicit operator int(TestEnum te)
    {
        Debug.Log("隐式转换");
        return te.length;
    }
    static public explicit operator int(TestEnum te)
    {
        Debug.Log("显示转换");
        return te.length;
    }

(当然,两个都写在这里是错误的)

 隐式转换是非常强力的编程工具,但是也可能带来比较难维护的问题吧感觉。
 
重载运算符
基本和C++的差不多,但是C#的相对来说灵活一点。比如说,+运算符的第一个参数不一定需要是这个类的对象。不像C++里面那样对于二目运算符里的左操作数对类型有要求。(比如<<这个运算符,重载IO流还需要声明是friend)
上个例
static public  TestEnum operator +(int t1,TestEnum t2)
    {
        return t1+t2.length;
    }

(这个功能太强了)

当然,这么写的前提是,我定义了从int到TestEnum的隐式转换

    static public implicit operator TestEnum(int i)
    {
        return new TestEnum(i);
    }

(所以说隐式转换功力很强劲啊…………)

 

 

委托和事件:

之前在《u3d脚本编程》这本书里了解了一些有关知识,再看一遍MSDN上的也就相当于复习。

摘一些MSDN上的TIPs好了,也不能说现在完全理解吧,慢慢理解还是可以的。先码

 

委托在以下情况下很有用:

  • 调用单个方法。
  • 一个类可能希望有方法规范的多个实现。
  • 希望允许使用静态方法实现规范。
  • 希望类似事件的设计模式。
  • 调用方不需要知道或获得在其上定义方法的对象。
  • 实现的提供程序希望只对少数选择组件“分发”规范实现。
  • 需要方便的组合。

接口在以下情况下很有用:

  • 规范定义将调用的一组相关方法。
  • 类通常只实现规范一次。
  • 接口的调用方希望转换为接口类型或从接口类型转换,以获得其他接口或类。

 

事件按照.NET的标准来的话,就是最好使用EventHandler,或者它的泛型版本(泛型版本是改变了EventArg的类型,一般是继承了EventArg的类(所以为什么不也做一个Object的泛型安全类型版本呢…………))

下面是一个简单的示例

class testClass : MonoBehaviour
{

    void Start()
    {

        DataElement data1 = new DataElement("alex", 15);
        EventListener eva = new EventListener(data1);
        
        data1.Name = "tom";
        data1.Age = 13;
    }
}

    class DataElementMessage : EventArgs
    {
        public enum ChangeType
        {
            NAME, AGE, BOTH, NONE
        }
        ChangeType changeType;

        internal ChangeType ChangeType1
        {
            get
            {
                return changeType;
            }

            set
            {
                changeType = value;
            }
        }

        public DataElementMessage(ChangeType changetypes)
        {
            this.ChangeType1 = changetypes;
        }
    }

    class DataElement
    {
        private string name;
        private int age;

        public string Name
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
                Onchange(new DataElementMessage(DataElementMessage.ChangeType.NAME));
            }
        }

        public int Age
        {
            get
            {
                return age;
            }

            set
            {
                age = value;
                Onchange(new DataElementMessage(DataElementMessage.ChangeType.AGE));
            }
        }

        public event EventHandler<DataElementMessage> change;



        void Onchange(DataElementMessage e)
        {
            if (change != null)
            {
                change(this, e);
            }
        }

        public DataElement(string name, int age)
        {
            this.Name = name;
            this.Age = age;
            Onchange(new DataElementMessage(DataElementMessage.ChangeType.BOTH));
        }



    }
    class EventListener
    {

        DataElement _eventsender;


       public EventListener(DataElement _e)
        {
            this._eventsender = _e;
            _e.change += ListChange;
        }

        public void ListChange(object sender, DataElementMessage message)
        {
            DataElement data = sender as DataElement;
            Debug.Log("changeType:" + message.ChangeType1.ToString() + "   Age:" + data.Age + "  Name:" + data.Name);
        }




    }

(当然这个实例没有写Detach,要写也比较容易把,写这个也就是熟悉一下基本框架。注意与MVC的联合思考就好)

 

显式实现接口:

怎么显式实现借口VS已经在自动补全里告诉你了。这里的关键是使用。

如果一个接口是显式实现的,like this

interface testInterface
{
    float Floating();
}
class FloatInterface : testInterface
{
    float testInterface.Floating()
    {
        return 2f;
    }
}

那么使用的时候,就必须用接口对象来实现,like this

        FloatInterface I = new FloatInterface();
        testInterface i = (testInterface)I;
        
       Debug.Log( i.Floating());

如果是非显式实现的,那么用FloatInterface的实例对象也可以进行接口调用,反之则不行。

那么这个东西有什么用呢?官方例子里大概意思这么写了一个例子,我也简化随便写了下

interface DoubleInterFace
{
    float Floating();
}
interface TrippleInterFace
{
    float Floating();
}
class FloatInterface : DoubleInterFace, TrippleInterFace
{
    float DoubleInterFace.Floating()
    {
        return 2f;
    }

    float TrippleInterFace.Floating()
    {
        return 3f;
    }
  public float Floating()
    {
        return 1f;
    }
}

class testClass : MonoBehaviour
{

    void Start()
    {
        FloatInterface _1f = new FloatInterface();
        TrippleInterFace _3f = _1f;
        DoubleInterFace _2f = _1f;
        Debug.Log(_1f.Floating());
        Debug.Log(_2f.Floating());
        Debug.Log(_3f.Floating());

    }
}

输出当然是1 2 3

 

 

 

编程笔记

C#编程笔记一