CSharp知识点整理记录

///Events Add and Remove

In the previous lesson I showed you how the compiler actually implements events by actually adding two methods named addon() and removeon() in the background and making the Action field private, so we can’t invoke it. But that was done in MSIL language, and we really don’t need to deal with such a low level. In this lesson, I will show you how we can do the exact same thing using the usual C# language, in case we need more control over how our events are implemented.

Let’s take again the code that we had in the previous lesson:

1

2

3

4

5

6

7

8

9

using System;

namespace HelloWorld

{    

    public class TrainSignal

    {

        public Action TrainsArecoming;

    }

}

And now, let’s re-trace all the steps that the compiler did automatically – first, let’s make our Action private, so it can’t be invoked directly:

1

2

3

4

5

6

7

8

9

using System;

namespace HelloWorld

{    

    public class TrainSignal

    {

        private Action trainsArecoming;

    }

}

I’ve also changed the name of the Action to start with a lowercase character, just so we won’t have a naming conflict later on.

Step two – add a public event:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

using System;

namespace HelloWorld

{    

    public class TrainSignal

    {

        private Action trainsArecoming;

        public event Action TrainsArecoming

        {

            add { }

            remove { }

        }

    }

}

You will certainly notice the new way of declaring my delegate: suddenly, my event declaration has a body, and inside it, I am adding two constructs, add and remove. This is the C# way of allowing us to do just the same thing as addon() and removeon() were doing automatically. It is in these constructs that we can subscribe and unsubscribe to our private Action, by using a ghost parameter named value (also present when declaring a property). To test this, let’s test that these constructs are executed when something subscribes or unsubscribes to our events:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

using System;

namespace HelloWorld

{

    public class TrainSignal

    {

        private Action trainsArecoming;

        public event Action TrainsArecoming

        {

            add

            {

                // add subscriber to the private Action

                trainsArecoming += value;

                Console.WriteLine("Subscriber was added!");

            }

            remove

            {

                // remove subscriber from the private Action

                trainsArecoming -= value;

                Console.WriteLine("Subscriber was removed!");

            }

        }

        public void TriggerTrainSignal()

        {

            // call all subscribers

            if (trainsArecoming != null) // make sure there are subscribers!

                trainsArecoming(); // invoke the Action

        }

    }

    public class Program

    {

        static void Main()

        {

            TrainSignal trainSignal = new TrainSignal();

            // add an event handler (a subscribing method that will be called when the event is triggered)

            trainSignal.TrainsArecoming += () => Console.WriteLine("I was notified by the event!");

            // trigger the train signal

            trainSignal.TriggerTrainSignal();

        }

    }

}

The explanation is simple: we are subscribing a method to a private Action by using the add and remove event constructs, which is the exact same thing that the compiler does automatically, if we don’t specify add and remove blocks. But, by specifying them, we have a greater control, because we can suddenly implement logic when adding or removing to an event, like, for example, checking if the subscriber method meets some requirements, or logging the actions, or subscribing the method more than once, or whatever.

The result of the above code will look like this:

 

At this point, you can kind of start to see that the event is just an interface towards our private Action, so that we are in control of who and when invokes that Action, and by using the add and remove event members, we can also control who can subscribe and unsubscribe to the Action, when it can do so, under what conditions, etc. This really gives us all the control we could want when it comes to securely playing with delegates.

True, most of the times, you won’t need this degree of control, using an event in a simple manner, like in the previous lesson, is more than enough for most daily life cases.

//一些关于Static的记录

注意:当类第一次被加载时,会对类中的静态变量先按顺序进行分配内存空间,当全部分配完内存空间之后,在对静态变量按顺序赋值。
首先分为两部分 寄存器和内存(包括缓存)
内存分为两部分 代码和数据
数据分为两部分 静态存储区和运行时存储
运行时存储分为 堆栈 和 堆
静态存储分为 全局静态存储 和 常量

Enum.GetUnderlyingType(Type) Method

using System;

public class Example

{

   public static void Main()

   {

      Enum[] enumValues = { ConsoleColor.Red, DayOfWeek.Monday,

                            MidpointRounding.ToEven, PlatformID.Win32NT,

                            DateTimeKind.Utc, StringComparison.Ordinal };

      Console.WriteLine("{0,-10} {1, 18}   {2,15}\n",

                        "Member", "Enumeration", "Underlying Type");

      foreach (var enumValue in enumValues)

         DisplayEnumInfo(enumValue);

   }

   static void DisplayEnumInfo(Enum enumValue)

   {

      Type enumType = enumValue.GetType();

      Type underlyingType = Enum.GetUnderlyingType(enumType);

      Console.WriteLine("{0,-10} {1, 18}   {2,15}",

                        enumValue, enumType.Name, underlyingType.Name);  

   }

}

// The example displays the following output:

//       Member            Enumeration   Underlying Type

//      

//       Red              ConsoleColor             Int32

//       Monday              DayOfWeek             Int32

//       ToEven       MidpointRounding             Int32

//       Win32NT            PlatformID             Int32

//       Utc              DateTimeKind             Int32

//       Ordinal      StringComparison             Int32

enum Foo : long { One, Two };

And then GetUnderlyingType is going to return long for typeof(Foo).

Note that the underlying type can be any integral type except for char types.

获得大小:Marshal.SizeOf(Enum.GetUnderlyingType(enumType))

/ HashSet vs. List //

A lot of people are saying that once you get to the size where speed is actually a concern that HashSet<T> will always beat List<T>, but that depends on what you are doing.

Let's say you have a List<T> that will only ever have on average 5 items in it. Over a large number of cycles, if a single item is added or removed each cycle, you may well be better off using a List<T>.

I did a test for this on my machine, and, well, it has to be very very small to get an advantage from List<T>. For a list of short strings, the advantage went away after size 5, for objects after size 20.

1 item LIST strs time: 617ms

1 item HASHSET strs time: 1332ms

2 item LIST strs time: 781ms

2 item HASHSET strs time: 1354ms

3 item LIST strs time: 950ms

3 item HASHSET strs time: 1405ms

4 item LIST strs time: 1126ms

4 item HASHSET strs time: 1441ms

5 item LIST strs time: 1370ms

5 item HASHSET strs time: 1452ms

6 item LIST strs time: 1481ms

6 item HASHSET strs time: 1418ms

7 item LIST strs time: 1581ms

7 item HASHSET strs time: 1464ms

8 item LIST strs time: 1726ms

8 item HASHSET strs time: 1398ms

9 item LIST strs time: 1901ms

9 item HASHSET strs time: 1433ms

1 item LIST objs time: 614ms

1 item HASHSET objs time: 1993ms

4 item LIST objs time: 837ms

4 item HASHSET objs time: 1914ms

7 item LIST objs time: 1070ms

7 item HASHSET objs time: 1900ms

10 item LIST objs time: 1267ms

10 item HASHSET objs time: 1904ms

13 item LIST objs time: 1494ms

13 item HASHSET objs time: 1893ms

16 item LIST objs time: 1695ms

16 item HASHSET objs time: 1879ms

19 item LIST objs time: 1902ms

19 item HASHSET objs time: 1950ms

22 item LIST objs time: 2136ms

22 item HASHSET objs time: 1893ms

25 item LIST objs time: 2357ms

25 item HASHSET objs time: 1826ms

28 item LIST objs time: 2555ms

28 item HASHSET objs time: 1865ms

31 item LIST objs time: 2755ms

31 item HASHSET objs time: 1963ms

34 item LIST objs time: 3025ms

34 item HASHSET objs time: 1874ms

37 item LIST objs time: 3195ms

37 item HASHSET objs time: 1958ms

40 item LIST objs time: 3401ms

40 item HASHSET objs time: 1855ms

43 item LIST objs time: 3618ms

43 item HASHSET objs time: 1869ms

46 item LIST objs time: 3883ms

46 item HASHSET objs time: 2046ms

49 item LIST objs time: 4218ms

49 item HASHSET objs time: 1873ms

Here is that data displayed as a graph:

Here's the code:

static void Main(string[] args)

{

    int times = 10000000;

    for (int listSize = 1; listSize < 10; listSize++)

    {

        List<string> list = new List<string>();

        HashSet<string> hashset = new HashSet<string>();

        for (int i = 0; i < listSize; i++)

        {

            list.Add("string" + i.ToString());

            hashset.Add("string" + i.ToString());

        }

        Stopwatch timer = new Stopwatch();

        timer.Start();

        for (int i = 0; i < times; i++)

        {

            list.Remove("string0");

            list.Add("string0");

        }

        timer.Stop();

        Console.WriteLine(listSize.ToString() + " item LIST strs time: " + timer.ElapsedMilliseconds.ToString() + "ms");

        timer = new Stopwatch();

        timer.Start();

        for (int i = 0; i < times; i++)

        {

            hashset.Remove("string0");

            hashset.Add("string0");

        }

        timer.Stop();

        Console.WriteLine(listSize.ToString() + " item HASHSET strs time: " + timer.ElapsedMilliseconds.ToString() + "ms");

        Console.WriteLine();

    }

    for (int listSize = 1; listSize < 50; listSize+=3)

    {

        List<object> list = new List<object>();

        HashSet<object> hashset = new HashSet<object>();

        for (int i = 0; i < listSize; i++)

        {

            list.Add(new object());

            hashset.Add(new object());

        }

        object objToAddRem = list[0];

        Stopwatch timer = new Stopwatch();

        timer.Start();

        for (int i = 0; i < times; i++)

        {

            list.Remove(objToAddRem);

            list.Add(objToAddRem);

        }

        timer.Stop();

        Console.WriteLine(listSize.ToString() + " item LIST objs time: " + timer.ElapsedMilliseconds.ToString() + "ms");

        timer = new Stopwatch();

        timer.Start();

        for (int i = 0; i < times; i++)

        {

            hashset.Remove(objToAddRem);

            hashset.Add(objToAddRem);

        }

        timer.Stop();

        Console.WriteLine(listSize.ToString() + " item HASHSET objs time: " + timer.ElapsedMilliseconds.ToString() + "ms");

        Console.WriteLine();

    }

    Console.ReadLine();

}

It's essentially pointless to compare two structures for performance that behave differently. Use the structure that conveys the intent. Even if you say your List<T> wouldn't have duplicates and iteration order doesn't matter making it comparable to a HashSet<T>, its still a poor choice to use List<T>because its relatively less fault tolerant.

Whether to use a HashSet<T> or List<T> comes down to how you need to access your collection. If you need to guarantee the order of items, use a List. If you don't, use a HashSet. Let Microsoft worry about the implementation of their hashing algorithms and objects.

A HashSet will access items without having to enumerate the collection (complexity of O(1) or near it), and because a List guarantees order, unlike a HashSet, some items will have to be enumerated (complexity of O(n)).

  • HashSet.Add will skip a new item if it’s deemed equal to one of the existing items and return false.
  • Dictionary.Add will throw an exception if the new key being added is deemed equal to one of the existing keys. However, if you use the Dictionary‘s indexer instead, it will replace the existing item if the new item is deemed equal to it.
  • List.Add will simply add the same item twice.
  • HashSet provides some very useful methods such as IsSubsetOf and Overlaps, both can be achieved on the other collection types using LINQ but HashSet provides an optimized, ready-made solution

The Original Collections: System.Collections namespace

The original collection classes are largely considered deprecated by developers and by Microsoft itself. In fact they indicate that for the most part you should always favor the generic or concurrent collections, and only use the original collections when you are dealing with legacy .NET code.

Because these collections are out of vogue, let's just briefly mention the original collection and their generic equivalents:

  • ArrayList
    • A dynamic, contiguous collection of objects.
    • Favor the generic collection List<T> instead.
  • Hashtable
    • Associative, unordered collection of key-value pairs of objects.
    • Favor the generic collection Dictionary<TKey,TValue> instead.
  • Queue
    • First-in-first-out (FIFO) collection of objects.
    • Favor the generic collection Queue<T> instead.
  • SortedList
    • Associative, ordered collection of key-value pairs of objects.
    • Favor the generic collection SortedList<T> instead.
  • Stack
    • Last-in-first-out (LIFO) collection of objects.
    • Favor the generic collection Stack<T> instead.

In general, the older collections are non-type-safe and in some cases less performant than their generic counterparts. Once again, the only reason you should fall back on these older collections is for backward compatibility with legacy code and libraries only.

The Concurrent Collections: System.Collections.Concurrent namespace

The concurrent collections are new as of .NET 4.0 and are included in the System.Collections.Concurrent namespace. These collections are optimized for use in situations where multi-threaded read and write access of a collection is desired.

The concurrent queue, stack, and dictionary work much as you'd expect. The bag and blocking collection are more unique. Below is the summary of each with a link to a blog post I did on each of them.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值