59、C编程:从基础概念到实践操作

C#编程:从基础概念到实践操作

1. 理解C#编程的关键术语和缩写

在C#编程中,有许多关键的术语和缩写对于理解程序的运行环境至关重要。以下是一些常见的C#相关缩写及其定义和描述:
| 缩写 | 定义 | 描述 |
| — | — | — |
| .NET | 无 | 微软对整个CLI堆栈的实现,包括CLR、CIL和各种语言,所有这些都符合CLS规范。 |
| BCL | 基类库 | CLI规范的一部分,定义了构建几乎所有程序所需的集合、线程、控制台和其他基类。 |
| C# | 无 | 一种编程语言。需要注意的是,除了CLI标准外,还有一个C#语言规范,也得到了ECMA和ISO标准机构的批准。 |
| CIL (IL) | 通用中间语言 | CLI规范的语言,定义了在CLI实现上可执行代码的指令。有时也称为IL或Microsoft IL (MSIL),以区别于其他中间语言。为了表明它是一个比微软更广泛的标准,首选CIL而不是MSIL甚至IL。 |
| CLI | 通用语言基础结构 | 定义中间语言、基类和行为特征的规范,使实现者能够创建虚拟执行系统和编译器,在这些系统和编译器中,源语言可以在一个通用的执行环境上进行互操作。 |
| CLR | 通用语言运行时 | 微软对CLI规范中定义的运行时的实现。 |
| CLS | 通用语言规范 | CLI规范的一部分,定义了源语言必须支持的核心功能子集,以便在根据CLI规范实现的运行时上执行。 |
| CTS | 通用类型系统 | 通常由符合CLI的语言实现的标准,定义了语言在模块外部可见地暴露的类型的表示和行为。它包括类型如何组合形成新类型的概念。 |
| FCL | .NET框架类库 | 构成微软.NET框架的类库。它包括微软对BCL的实现以及大量用于Web开发、分布式通信、数据库访问、富客户端用户界面开发等的类库。 |
| VES (runtime) | 虚拟执行系统 | 管理为CLI编译的程序执行的代理。 |

这些术语和缩写构成了C#编程的基础,对于深入理解和开发C#程序具有重要意义。

2. 下载和安装C#编译器及CLI平台

要编译和运行C#程序,需要安装编译器和CLI平台的版本。以下是不同平台的安装方法:

2.1 Microsoft .NET

Microsoft .NET是主要的CLI平台,也是在Microsoft Windows上进行开发的首选平台。
- 最低安装要求 :包括编译器和支持C# 2.0语法的.NET框架的最小安装是.NET Framework 2.0或更高版本的可再发行包。可以从http://msdn.microsoft.com/en-us/netframework/default.aspx下载。
- 安装Visual Studio IDE :如果需要一个包含智能感知和项目文件支持的丰富IDE,可以安装Visual Studio IDE的一个版本。其中Visual C# Express可以从http://lab.msdn.microsoft.com/express免费下载。

2.2 设置编译器路径

对于命令行编译,无论是否安装了Visual Studio,都必须设置PATH环境变量以包含C#编译器CSC.EXE。
- 安装了Visual Studio .NET :如果计算机上安装了Visual Studio .NET,可以从开始菜单中选择“所有程序” -> “Microsoft Visual Studio .NET” -> “Visual Studio工具” -> “Visual Studio命令提示”来打开命令提示符。这个命令提示符会将CSC.EXE添加到路径中,以便从任何目录执行。
- 未安装Visual Studio .NET :如果没有安装Visual Studio .NET,开始菜单中不会出现特殊的编译器命令提示符项。此时需要显式引用完整的编译器路径名或将其添加到路径中。编译器位于%Windir%\Microsoft.NET\Framework\ ,其中 是.NET框架的版本(如v1.0.3705、v1.1.4322、v2.0.50727等),%Windir%是指向Windows目录位置的环境变量。可以使用以下命令将该位置添加到路径中:

Set PATH=%PATH%;%Windir%\Microsoft.NET\Framework\v2.0.50727

将 替换为实际的版本号。一旦路径中包含了框架,就可以在不提供完整路径的情况下使用.NET C#编译器CSC.EXE。

2.3 Mono

对于非Microsoft Windows平台上的CLI开发,可以考虑使用Mono,它可以从www.mono-project.com下载。与.NET平台一样,如果C#编译器不在搜索路径中,Mono也需要完整的路径。
- Linux默认安装路径 :在Linux上,默认的安装路径是/usr/lib/mono/ ,编译器是gmcs.exe或mcs.exe,具体取决于版本。
- Windows默认安装路径 :如果Mono安装在Microsoft Windows上,默认路径是%ProgramFiles%\Mono - \lib\mono\ \。
- 编译示例 :使用Mono平台的编译器编译HelloWorld.cs的命令如下:

C:\SAMPLES>msc.exe HelloWorld.cs

不幸的是,Linux环境不能直接运行生成的二进制文件,而是需要使用mono.exe显式执行运行时:

C:\SAMPLES>mono.exe HelloWorld.exe 
Hello. My name is Inigo Montoya.

2.4 安装流程总结

graph LR
    A[选择平台] --> B{Microsoft Windows}
    B -- 是 --> C[安装Microsoft .NET]
    C --> D[设置编译器路径]
    B -- 否 --> E[安装Mono]
    E --> F[设置编译器路径]

通过以上步骤,你可以成功下载和安装C#编译器及CLI平台,为后续的C#编程做好准备。

3. 完整源代码清单示例

3.1 井字棋游戏(Tic - Tac - Toe)

以下是一个简单的井字棋游戏的源代码:

#define CSHARP2
using System;
#pragma warning disable 1030 // Disable user-defined warnings
// The TicTacToe class enables two players to 
// play tic-tac-toe. 
class TicTacToeGame      // Declares the TicTacToeGame class 
{
    static void Main()  // Declares the entry point to the program
    {
        // Stores locations each player has moved.
        int[] playerPositions = { 0, 0 };
        // Initially set the currentPlayer to Player 1;
        int currentPlayer = 1;
        // Winning player
        int winner = 0;
        string input = null;
        // Display the board and 
        // prompt the current player
        // for his next move.
        for (int turn = 1; turn <= 10; ++turn)
        {
            DisplayBoard(playerPositions);
            #region Check for End Game
            if (EndGame(winner, turn, input))
            {
                break;
            }
            #endregion Check for End Game
            input = NextMove(playerPositions, currentPlayer);
            winner = DetermineWinner(playerPositions);
            // Switch players
            currentPlayer = (currentPlayer == 2) ? 1 : 2;
        }
    }
    private static string NextMove(int[] playerPositions,
                   int currentPlayer)
    {
        string input;
        // Repeatedly prompt the player for a move
        // until a valid move is entered.
        bool validMove;
        do
        {
            // Request a move from the current player.
            System.Console.Write("\nPlayer {0} - Enter move:",
                       currentPlayer);
            input = System.Console.ReadLine();
            validMove = ValidateAndMove(playerPositions,
                          currentPlayer, input);
        } while (!validMove);
        return input;
    }

    static bool EndGame(int winner, int turn, string input)
    {
        bool endGame = false;
        if (winner > 0)
        {
            System.Console.WriteLine("\nPlayer {0} has won!!!!", winner);
            endGame = true;
        }
        else if (turn == 10)
        {
            // After completing the 10th display of the
            // board, exit out rather than prompting the
            // user again.
            System.Console.WriteLine("\nThe game was a tie!"); 
            endGame = true;
        }
        else if (input == "" || input == "quit")
        {
            // Check if user quit by hitting Enter without 
            // any characters or by typing "quit".
            System.Console.WriteLine("The last player quit");
            endGame = true;
        }
        return endGame;
    }
    static int DetermineWinner(int[] playerPositions)
    {
        int winner = 0;
        // Determine if there is a winner
        int[] winningMasks = {
            7, 56, 448, 73, 146, 292, 84, 273};
        foreach (int mask in winningMasks)
        {
            if ((mask & playerPositions[0]) == mask)
            {
                winner = 1;
                break;
            }
            else if ((mask & playerPositions[1]) == mask)
            {
                winner = 2;
                break;
            }
        }
        return winner;
    }
    static bool ValidateAndMove(
        int[] playerPositions, int currentPlayer, string input)
    {
        bool valid = false;
        // Check the current player’s input.
        switch (input)
        {
            case "1":
            case "2":
            case "3":
            case "4":
            case "5":
            case "6":
            case "7":
            case "8":
            case "9": 
#warning  "Same move allowed multiple times."
                int shifter;  // The number of places to shift 
                // over in order to set a bit.
                int position;  // The bit which is to be set.
                // int.Parse() converts "input" to an integer.
                // "int.Parse(input) – 1" because arrays 
                // are zero-based.
                shifter = int.Parse(input) - 1;
                // Shift mask of 00000000000000000000000000000001
                // over by cellLocations.
                position = 1 << shifter;
                // Take the current player cells and OR them 
                // to set the new position as well.
                // Since currentPlayer is either 1 or 2 you 
                // subtract one to use currentPlayer as an
                // index in a 0-based array.
                playerPositions[currentPlayer - 1] |= position;
                valid = true;
                break;
            case "":
            case "quit":
                valid = true;
                break;
            default:
                // If none of the other case statements
                // is encountered, then the text is invalid.
                System.Console.WriteLine(
                    "\nERROR:  Enter a value from 1-9. "
                    + "Push ENTER to quit");
                break;
        }
        return valid;
    }
    static void DisplayBoard(int[] playerPositions)
    {
        // This represents the borders between each cell
        // for one row.
        string[] borders = {
            "|", "|", "\n---+---+---\n", "|", "|",
            "\n---+---+---\n", "|", "|", ""
        };
        // Display the current board;
        int border = 0;  // set the first border (border[0] = "|")
#if CSHARP2
        System.Console.Clear(); 
#endif
        for (int position = 1;
             position <= 256;
             position <<= 1, border++)
        {
            char token = CalculateToken(
                playerPositions, position);
            // Write out a cell value and the border that 
            // comes after it.
            System.Console.Write(" {0} {1}", 
                token, borders[border]);
        }
    }
    static char CalculateToken(
        int[] playerPositions, int position)
    {
        // Initialize the players to 'X' and 'O'
        char[] players = {'X', 'O'};
        char token;
        // If player has the position set, 
        // then set the token to that player.
        if ((position & playerPositions[0]) == position)
        {
            // Player 1 has that position marked
            token = players[0];
        }
        else if ((position & playerPositions[1]) == position)
        {
            // Player 2 has that position marked
            token = players[1];
        }
        else
        {
            // The position is empty.
            token = ' ';
        }
        return token;
    }
    #line 113 "TicTacToe.cs"
    // Generated code goes here
    #line default 
}

这个程序实现了一个简单的井字棋游戏,允许两个玩家轮流下棋,直到有一方获胜或平局。

3.2 产品序列号类(ProductSerialNumber)

以下是一个表示产品序列号的类的源代码:

public sealed class ProductSerialNumber 
{
    public ProductSerialNumber(
        string productSeries, int model, long id)
    {
        ProductSeries = productSeries;
        Model = model;
        Id = id;
    }
    public readonly string ProductSeries;
    public readonly int Model;
    public readonly long Id;
    public override int GetHashCode()
    {
        int hashCode = ProductSeries.GetHashCode();
        hashCode ^= Model;  // Xor (eXclusive OR)
        hashCode ^= Id.GetHashCode();  // Xor (eXclusive OR)
        return hashCode;
    }
    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        if (this.GetType() != obj.GetType())
        {
            return false;
        }
        return Equals((ProductSerialNumber)obj);
    }
    public bool Equals(ProductSerialNumber obj)
    {
        // STEP 3: Possibly check for equivalent hash codes
        // if (this.GetHashCode() != obj.GetHashCode())
        // {
        //    return false;
        // } 
        // STEP 4: Check base.Equals if base overrides Equals()
        // System.Diagnostics.Debug.Assert(
        //     base.GetType() != typeof(object));
        // if ( base.Equals(obj) )
        // {
        //    return false;
        // } 
        // STEP 1: Check for null
        return ((obj != null)
            // STEP 5: Compare identifying fields for equality.
            && (ProductSeries == obj.ProductSeries) &&
            (Model == obj.Model) &&
            (Id == obj.Id));
    }
    public static bool operator ==(
        ProductSerialNumber leftHandSide,
        ProductSerialNumber rightHandSide)
    {
        // Check if leftHandSide is null.  
        // (operator== would be recursive)
        if (ReferenceEquals(leftHandSide, null))
        {
            // Return true if rightHandSide is also null
            // but false otherwise.
            return ReferenceEquals(rightHandSide, null);
        }
        return (leftHandSide.Equals(rightHandSide));
    }
    public static bool operator !=(
        ProductSerialNumber leftHandSide,
        ProductSerialNumber rightHandSide)
    {
        return !(leftHandSide == rightHandSide);
    } 
}

这个类用于表示产品的序列号,并实现了哈希码和相等性比较的方法。

3.3 二叉树和对(Binary Tree and Pair)

以下是实现二叉树和对的相关代码:

public enum PairItem 
{
    First,
    Second 
}
interface IPair<T> 
{
    T First
    {
        get;
        set;
    }
    T Second
    {
        get;
        set;
    }
    T this[PairItem index]
    {
        get;
        set;
    } 
}
using System.Collections; 
using System.Collections.Generic;
public struct Pair<T> : IPair<T>, IEnumerable<T> 
{
    public Pair(T first)
    {
        _First = first;
        _Second = default(T);
    }
    public Pair(T first, T second)
    {
        _First = first;
        _Second = second;
    }
    public T First
    {
        get
        {
            return _First;
        }
        set
        {
            _First = value;
        }
    }
    private T _First;
    public T Second
    {
        get
        {
            return _Second;
        }
        set
        {
            _Second = value;
        }
    }
    private T _Second;
    [System.Runtime.CompilerServices.IndexerName("Entry")]
    public T this[PairItem index]
    {
        get
        {
            switch (index)
            {
                case PairItem.First:
                    return First;
                case PairItem.Second:
                    return Second;
                default:
                    throw new NotImplementedException(
                        string.Format(
                        "The enum {0} has not been implemented",
                        index.ToString()));
            }
        }
        set
        {
            switch (index)
            {
                case PairItem.First:
                    First = value;
                    break;
                case PairItem.Second:
                    Second = value;
                    break;
                default:
                    throw new NotImplementedException(
                        string.Format(
                        "The enum {0} has not been implemented",
                        index.ToString()));
            }
        }
    }
    #region IEnumerable<T> Members
    public IEnumerator<T> GetEnumerator()
    {
        yield return First;
        yield return Second;
    }
    #endregion
    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    #endregion
    public IEnumerable<T> GetReverseEnumerator()
    {
        yield return Second;
        yield return First;
    }
    // Listing 12.24
    public IEnumerable<T> GetNotNullEnumerator()
    {
        if ((First == null) || (Second == null))
        {
            yield break;
        }
        yield return Second;
        yield return First;
    } 
}
using System.Collections; 
using System.Collections.Generic;
public interface IBinaryTree<T> 
{
    T Item
    {
        get;
        set;
    }
    Pair<IBinaryTree<T>> SubItems
    {
        get;
        set;
    } 
} 
public class BinaryTree<T> : IEnumerable<T> 
{
    public BinaryTree(T value)
    {
        Value = value;
    }
    public T Value
    {
        get { return _Value; }
        set { _Value = value; }
    }
    private T _Value;
    public Pair<BinaryTree<T>> SubItems
    {
        get { return _SubItems; }
        set
        {
            IComparable first;
            first = (IComparable)value.First.Value;
            if (first.CompareTo(value.Second.Value) < 0)
            {
                // first is less than second.
            }
            else
            {
                // first and second are the same or
                // second is less than first.
            }
            _SubItems = value;
        }
    }
    private Pair<BinaryTree<T>> _SubItems;
    public T this[params PairItem[] branches]
    {
        get
        {
            BinaryTree<T> currentNode = this;
            int totalLevels = 
                (branches == null) ? 0 : branches.Length;
            int currentLevel = 0;
            while (currentLevel < totalLevels)
            {
                currentNode = 
                    currentNode.SubItems[branches[currentLevel]];
                if (currentNode == null)
                {
                    // The binary tree at this location is null.
                    throw new IndexOutOfRangeException();
                }
                currentLevel++;
            }
            return currentNode.Value;
        }
    }
    #region IEnumerable<T>
    // Listing 12.22
    public IEnumerator<T> GetEnumerator()
    {
        // Return the item at this node.
        yield return Value;
        // Iterate through each of the elements in the pair.
        foreach (BinaryTree<T> tree in SubItems)
        {
            if (tree != null)
            {
                // Since each element in the pair is a tree,
                // traverse the tree and yield each
                // element.
                foreach (T item in tree)
                {
                    yield return item;
                }
            }
        }
    }
    #endregion IEnumerable<T>
    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    #endregion
}

这些代码定义了一个对(Pair)和一个二叉树(BinaryTree)的接口和实现,提供了遍历和操作的方法。

3.4 命令行属性处理(Command - Line Attributes)

以下是处理命令行属性的相关代码:

using System; 
using System.Diagnostics;
public partial class Program 
{
    public static void Main(string[] args)
    {
        string errorMessage;
        CommandLineInfo commandLine = new CommandLineInfo();
        if (!CommandLineHandler.TryParse(
            args, commandLine, out errorMessage))
        {
            Console.WriteLine(errorMessage);
            DisplayHelp();
        }
        if (commandLine.Help)
        {
            DisplayHelp();
        }
        else
        {
            if (commandLine.Priority !=
                ProcessPriorityClass.Normal)
            {
                // Change thread priority
            }
        }
        // ...
    }
    private static void DisplayHelp()
    {
        // Display the command-line help.
        Console.WriteLine(
            "Thankyou for contacting the help text");
    } 
}
using System; 
using System.Diagnostics;
public partial class Program 
{
    private class CommandLineInfo
    {
        [CommandLineSwitchAlias("?")]
        public bool Help
        {
            get { return _Help; }
            set { _Help = value; }
        }
        private bool _Help;
        [CommandLineSwitchRequired]
        [CommandLineSwitchAlias("FileName")]
        public string Out
        {
            get { return _Out; }
            set { _Out = value; }
        }
        private string _Out;
        public ProcessPriorityClass Priority
        {
            get { return _Priority; }
            set { _Priority = value; }
        }
        private ProcessPriorityClass _Priority =
            ProcessPriorityClass.Normal;
    } 
}
using System; 
using System.Diagnostics; 
using System.Reflection;
public class CommandLineHandler 
{
    public static void Parse(string[] args, object commandLine)
    {
        string errorMessage;
        if (!TryParse(args, commandLine, out errorMessage))
        {
            throw new ApplicationException(errorMessage);
        }
    }
    public static bool TryParse(string[] args, object commandLine,
        out string errorMessage)
    {
        bool success = false;
        errorMessage = null;
        foreach (string arg in args)
        {
            string option;
            if (arg[0] == '/' || arg[0] == '-')
            {
                string[] optionParts = arg.Split(
                    new char[] { ':' }, 2);
                // Remove the slash|dash
                option = optionParts[0].Remove(0, 1);
                PropertyInfo property =
                    commandLine.GetType().GetProperty(option,
                        BindingFlags.IgnoreCase |
                        BindingFlags.Instance |
                        BindingFlags.Public);
                if (property != null)
                {
                    if (property.PropertyType == typeof(bool))
                    {
                        // Last parameters for handling indexers
                        property.SetValue(
                            commandLine, true, null);
                        success = true;
                    }
                    else if (
                        property.PropertyType == typeof(string))
                    {
                        property.SetValue(
                            commandLine, optionParts[1], null);
                        success = true;
                    }
                    else if (property.PropertyType.IsEnum)
                    {
                        try
                        {
                            property.SetValue(commandLine,
                                Enum.Parse(
                                    typeof(ProcessPriorityClass),
                                    optionParts[1], true), 
                                null);
                            success = true;
                        }
                        catch (ArgumentException )
                        {
                            success = false;
                            errorMessage =
                                string.Format(
                                    "The option '{0}' is " +
                                    "invalid for '{1}'",
                                optionParts[1], option);
                        }
                    }
                    else
                    {
                        success = false;
                        errorMessage = string.Format(
                            "Data type '{0}' on {1} is not"
                            + " supported.",
                            property.PropertyType.ToString(),
                            commandLine.GetType().ToString());
                    }
                }
                else
                {
                    success = false;
                    errorMessage = string.Format(
                        "Option '{0}' is not supported.",
                        option);
                }
            }
        }
        return success;
    } 
}
using System; 
using System.Collections.Specialized; 
using System.Reflection;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public class CommandLineSwitchRequiredAttribute : Attribute 
{
    public static string[] GetMissingRequiredOptions(
        object commandLine)
    {
        StringCollection missingOptions = new StringCollection();
        PropertyInfo[] properties = 
            commandLine.GetType().GetProperties();
        foreach (PropertyInfo property in properties)
        {
            Attribute[] attributes = 
                 (Attribute[])property.GetCustomAttributes(
                    typeof(CommandLineSwitchRequiredAttribute),
                    false);
            if ((attributes.Length > 0) &&
                (property.GetValue(commandLine, null) == null))
            {
                if (property.GetValue(commandLine, null) == null)
                {
                    missingOptions.Add(property.Name);
                }
            }
        }
        string[] results = new string[missingOptions.Count];
        missingOptions.CopyTo(results, 0);
        return results;
    } 
}
using System; 
using System.Reflection; 
using System.Collections.Generic;
[AttributeUsage(AttributeTargets.Property)] 
public class CommandLineSwitchAliasAttribute : Attribute 
{
    public CommandLineSwitchAliasAttribute(string alias)
    {
        Alias = alias;
    }
    public string Alias
    {
        get { return _Alias; }
        set { _Alias = value; }
    }
    private string _Alias;
    public static Dictionary<string, PropertyInfo> GetSwitches(
        object commandLine)
    {
        PropertyInfo[] properties = null;
        Dictionary<string, PropertyInfo> options =
            new Dictionary<string, PropertyInfo>();
        properties = commandLine.GetType().GetProperties(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance);
        foreach (PropertyInfo property in properties)
        {
            options.Add(property.Name.ToLower(), property);
            foreach (CommandLineSwitchAliasAttribute attribute in
                property.GetCustomAttributes(
                typeof(CommandLineSwitchAliasAttribute), false))
            {
                options.Add(attribute.Alias.ToLower(), property);
            }
        }
        return options;
    } 
}
using System; 
using System.Reflection; 
using System.Collections.Generic;
public class CommandLineHandler 
{
    // ...
    public static bool TryParse(
        string[] args, object commandLine,
        out string errorMessage)
    {
        bool success = false;
        errorMessage = null;
        Dictionary<string, PropertyInfo> options =
            CommandLineSwitchAliasAttribute.GetSwitches(
                commandLine);
        foreach (string arg in args)
        {
            PropertyInfo property;
            string option;
            if (arg[0] == '/' || arg[0] == '-')
            {
                string[] optionParts = arg.Split(
                    new char[] { ':' }, 2);
                option = optionParts[0].Remove(0, 1).ToLower();
                if (options.TryGetValue(option, out property))
                {
                    success = SetOption(
                        commandLine, property,
                        optionParts, ref errorMessage);
                }
                else
                {
                    success = false;
                    errorMessage = string.Format(
                        "Option '{0}' is not supported.",
                        option);
                }
            }
        }
        return success;
    }
    private static bool SetOption(
        object commandLine, PropertyInfo property, 
        string[] optionParts, ref string errorMessage)
    {
        bool success;
        if (property.PropertyType == typeof(bool))
        {
            // Last parameters for handling indexers
            property.SetValue(
                commandLine, true, null);
            success = true;
        }
        else
        {
            if ((optionParts.Length < 2) 
                || optionParts[1] == ""
                || optionParts[1] == ":")
            {
                // No setting was provided for the switch.
                success = false;
                errorMessage = string.Format(
                    "You must specify the value for the {0} option.",
                    property.Name);
            }
            else if (
                property.PropertyType == typeof(string))
            {
                property.SetValue(
                    commandLine, optionParts[1], null);
                success = true;
            }
            else if (property.PropertyType.IsEnum)
            {
                success = TryParseEnumSwitch(
                    commandLine, optionParts, 
                    property, ref errorMessage);
            }
            else
            {
                success = false;
                errorMessage = string.Format(
                    "Data type '{0}' on {1} is not supported.",
                    property.PropertyType.ToString(),
                    commandLine.GetType().ToString());
            }
        }
        return success;
    } 
}

这段代码实现了对命令行参数的解析和处理,包括检查必填选项、处理别名等功能。

3.5 使用P/Invoke进行虚拟计算机检测(Virtual Computer Detection Using P/Invoke)

以下是使用P/Invoke进行虚拟计算机检测的代码:

using System.Runtime.InteropServices;
class Program 
{
    delegate void MethodInvoker();
    unsafe static int Main(string[] args)
    {
        // Assign redpill 
        byte[] redpill = { 
            0x0f, 0x01, 0x0d,       // asm SIDT instruction
            0x00, 0x00, 0x00, 0x00, // placeholder for an address
            0xc3};                  // asm return instruction
        unsafe
        {
            fixed (byte* matrix = new byte[6],
                redpillPtr = redpill)
            {
                // Move the address of matrix immediately 
                // following the SIDT instruction of memory.
                *(uint*)&redpillPtr[3] = (uint)&matrix[0];
                using (VirtualMemoryPtr codeBytesPtr = 
                    new VirtualMemoryPtr(redpill.Length))
                {
                    Marshal.Copy(
                        redpill, 0, 
                        codeBytesPtr, redpill.Length);
                    MethodInvoker method = 
                        (MethodInvoker)Marshal.GetDelegateForFunctionPointer(
                        codeBytesPtr, typeof(MethodInvoker));
                    method();
                }
                if (matrix[5] > 0xd0)
                {
                    Console.WriteLine("Inside Matrix! \n");
                    return 1;
                }
                else
                {
                    Console.WriteLine("Not in Matrix. \n");
                    return 0;
                }
            } // fixed
        } 
    }
}
public class VirtualMemoryPtr :
    System.Runtime.InteropServices.SafeHandle 
{
    public VirtualMemoryPtr(int memorySize) :
        base(IntPtr.Zero, true)
    {
        ProcessHandle = 
            VirtualMemoryManager.GetCurrentProcessHandle();
        MemorySize = (IntPtr)memorySize;
        AllocatedPointer = 
            VirtualMemoryManager.AllocExecutionBlock(
            memorySize, ProcessHandle);
        Disposed = false;
    }
    public readonly IntPtr AllocatedPointer;
    readonly IntPtr ProcessHandle;
    readonly IntPtr MemorySize;
    bool Disposed;
    public static implicit operator IntPtr(
        VirtualMemoryPtr virtualMemoryPointer)
    {
        return virtualMemoryPointer.AllocatedPointer;
    }
    // SafeHandle abstract member
    public override bool IsInvalid
    {
        get 
        { 
            return Disposed; 
        }
    }
    // SafeHandle abstract member
    protected override bool ReleaseHandle()
    {
        if (!Disposed)
        {
            Disposed = true;
            GC.SuppressFinalize(this);
            VirtualMemoryManager.VirtualFreeEx(ProcessHandle,
                AllocatedPointer, MemorySize);
        }
        return true;
    } 
}
class VirtualMemoryManager 
{
    /// <summary>
    /// The type of memory allocation. This parameter must
    /// contain one of the following values.
    /// </summary>
    [Flags]
    private enum AllocationType : uint
    {
        /// <summary>
        /// Allocates physical storage in memory or in the 
        /// paging file on disk for the specified reserved 
        /// memory pages. The function initializes the memory
        /// to zero. 
        /// </summary>
        Commit = 0x1000,
        /// <summary>
        /// Reserves a range of the process's virtual address 
        /// space without allocating any actual physical 
        /// storage in memory or in the paging file on disk. 
        /// </summary>
        Reserve = 0x2000,
        /// <summary>
        /// Indicates that data in the memory range specified by 
        /// lpAddress and dwSize is no longer of interest. The 
        /// pages should not be read from or written to the 
        /// paging file. However, the memory block will be used 
        /// again later, so it should not be decommitted. This 
        /// value cannot be used with any other value.
        /// </summary>
        Reset = 0x80000,
        /// <summary>
        /// Allocates physical memory with read-write access. 
        /// This value is solely for use with Address Windowing 
        /// Extensions (AWE) memory. 
        /// </summary>
        Physical = 0x400000,
        /// <summary>
        /// Allocates memory at the highest possible address. 
        /// </summary>
        TopDown = 0x100000,
    }
    /// <summary>
    /// The memory protection for the region of pages to be 
    /// allocated.
    /// </summary>
    [Flags]
    private enum ProtectionOptions : uint
    {
        /// <summary>
        /// Enables execute access to the committed region of 
        /// pages. An attempt to read or write to the committed 
        /// region results in an access violation.
        /// </summary>
        Execute = 0x10,
        /// <summary>
        /// Enables execute and read access to the committed 
        /// region of pages. An attempt to write to the 
        /// committed region results in an access violation.
        /// </summary>
        PageExecuteRead = 0x20,
        /// <summary>
        /// Enables execute, read, and write access to the 
        /// committed region of pages.
        /// </summary>
        PageExecuteReadWrite = 0x40,
        // ...
    }
    /// <summary>
    /// The type of free operation
    /// </summary>
    [Flags]
    private enum MemoryFreeType : uint
    {
        /// <summary>
        /// Decommits the specified region of committed pages. 
        /// After the operation, the pages are in the reserved 
        /// state. 
        /// </summary>
        Decommit = 0x4000,
        /// <summary>
        /// Releases the specified region of pages. After this 
        /// operation, the pages are in the free state. 
        /// </summary>
        Release = 0x8000
    }
    [DllImport("kernel32.dll", EntryPoint="GetCurrentProcess")]
    [DllImport("kernel32.dll")]
    internal static extern IntPtr GetCurrentProcess();
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr VirtualAllocEx(
        IntPtr hProcess,
        IntPtr lpAddress,
        IntPtr dwSize, 
        AllocationType flAllocationType,
        uint flProtect);
    // ...
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern IntPtr GetCurrentProcessHandle();
    static extern bool VirtualProtectEx(
        IntPtr hProcess, IntPtr lpAddress,
        IntPtr dwSize, uint flNewProtect,
        ref uint lpflOldProtect);
    public static IntPtr AllocExecutionBlock(
        int size, IntPtr hProcess)
    {
        IntPtr codeBytesPtr;
        codeBytesPtr = VirtualAllocEx(
            hProcess, IntPtr.Zero,
            (IntPtr)size,
            AllocationType.Reserve | AllocationType.Commit,
            (uint)ProtectionOptions.PageExecuteReadWrite);
        if (codeBytesPtr == IntPtr.Zero)
        {
            throw new System.ComponentModel.Win32Exception();
        }
        uint lpflOldProtect = 0;
        if (!VirtualProtectEx(
            hProcess, codeBytesPtr,
            (IntPtr)size,
            (uint)ProtectionOptions.PageExecuteReadWrite, 
            ref lpflOldProtect))
        {
            throw new System.ComponentModel.Win32Exception();
        }
        return codeBytesPtr;
    }
    public static IntPtr AllocExecutionBlock(int size)
    {
        return AllocExecutionBlock(
            size, GetCurrentProcessHandle());
    }
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool VirtualFreeEx(
        IntPtr hProcess, IntPtr lpAddress,
        IntPtr dwSize, IntPtr dwFreeType);
    public static bool VirtualFreeEx(
        IntPtr hProcess, IntPtr lpAddress,
        IntPtr dwSize)
    {
        bool result = VirtualFreeEx(
            hProcess, lpAddress, dwSize, 
            (IntPtr)MemoryFreeType.Decommit);
        if (!result)
        {
            throw new System.ComponentModel.Win32Exception();
        }
        return result;
    }
    public static bool VirtualFreeEx(
        IntPtr lpAddress, IntPtr dwSize)
    {
        return VirtualFreeEx(
            GetCurrentProcessHandle(), lpAddress, dwSize);
    }
}

这段代码通过P/Invoke调用Windows API,实现了对虚拟计算机环境的检测。

通过以上代码示例,你可以进一步了解C#编程在不同场景下的应用,加深对C#语言的理解和掌握。

4. 代码示例分析

4.1 井字棋游戏分析

井字棋游戏代码实现了一个简单的双人游戏。以下是对该代码的详细分析:
- 核心数据结构 :使用 int[] playerPositions 数组来记录每个玩家的落子位置,通过位运算来判断玩家是否占据了特定位置。
- 游戏流程控制 :通过 Main 方法中的 for 循环控制游戏的回合数,在每一回合中调用 DisplayBoard 显示棋盘, NextMove 获取玩家输入, DetermineWinner 判断是否有玩家获胜,最后根据情况切换玩家。
- 输入验证 ValidateAndMove 方法对玩家的输入进行验证,确保输入是有效的落子位置或退出指令。

4.2 产品序列号类分析

产品序列号类 ProductSerialNumber 用于表示产品的唯一标识。
- 属性 :包含 ProductSeries Model Id 三个只读属性,用于存储产品的系列、型号和编号。
- 哈希码和相等性比较 :重写了 GetHashCode Equals 方法,通过异或运算生成哈希码,并比较对象的属性来判断是否相等。同时,还实现了 == != 运算符的重载,方便进行对象的比较。

4.3 二叉树和对分析

这段代码定义了对( Pair )和二叉树( BinaryTree )的接口和实现。
- 对(Pair) :实现了 IPair<T> 接口,提供了 First Second 属性,以及索引器 this[PairItem index] 。同时,还实现了 IEnumerable<T> 接口,支持对元素的遍历。
- 二叉树(BinaryTree) :实现了 IBinaryTree<T> 接口,包含 Value 属性和 SubItems 属性,用于存储节点的值和子节点。通过索引器 this[params PairItem[] branches] 可以访问二叉树中的特定节点。

4.4 命令行属性处理分析

命令行属性处理代码实现了对命令行参数的解析和处理。
- 命令行信息类 CommandLineInfo 类定义了命令行参数的属性,使用 CommandLineSwitchAlias CommandLineSwitchRequired 属性来指定别名和必填项。
- 命令行处理类 CommandLineHandler 类提供了 Parse TryParse 方法,用于解析命令行参数。通过反射机制,根据参数名查找对应的属性,并设置属性值。
- 属性处理 CommandLineSwitchRequiredAttribute CommandLineSwitchAliasAttribute 类分别用于处理必填选项和别名,通过反射获取属性的自定义属性,实现对命令行参数的灵活处理。

4.5 使用P/Invoke进行虚拟计算机检测分析

这段代码通过P/Invoke调用Windows API,实现了对虚拟计算机环境的检测。
- 红药丸代码 :定义了一个包含汇编指令的字节数组 redpill ,通过修改指令中的地址,将其加载到内存中执行。
- 内存管理 VirtualMemoryPtr 类继承自 SafeHandle ,用于管理内存的分配和释放。通过 VirtualMemoryManager 类的静态方法,调用Windows API进行内存的分配、保护和释放。
- 检测逻辑 :执行红药丸代码后,根据内存中的特定位置的值判断是否处于虚拟计算机环境中。

graph LR
    A[代码示例] --> B{游戏类}
    A --> C{数据类}
    A --> D{数据结构类}
    A --> E{命令行处理类}
    A --> F{系统交互类}
    B --> G[井字棋游戏]
    C --> H[产品序列号类]
    D --> I[二叉树和对]
    E --> J[命令行属性处理]
    F --> K[虚拟计算机检测]

5. 总结与建议

5.1 总结

本文介绍了C#编程中的关键术语和缩写,详细阐述了C#编译器及CLI平台的下载和安装方法,包括Microsoft .NET和Mono平台。同时,提供了多个完整的源代码清单示例,涵盖了游戏开发、数据表示、数据结构和命令行处理等多个方面。通过对这些代码示例的分析,深入了解了C#编程在不同场景下的应用和实现方法。

5.2 建议

  • 深入学习基础知识 :C#编程中的关键术语和缩写是理解和开发C#程序的基础,建议深入学习这些知识,掌握其含义和用途。
  • 多实践代码示例 :通过实践代码示例,可以加深对C#语言的理解和掌握。建议按照本文提供的代码示例进行实践,并尝试对代码进行修改和扩展,以提高编程能力。
  • 关注性能和安全 :在开发C#程序时,要关注性能和安全问题。例如,在使用P/Invoke调用Windows API时,要注意内存管理和异常处理,避免出现安全漏洞。
  • 持续学习和更新 :C#语言和相关技术不断发展和更新,建议持续学习和关注最新的技术动态,不断提升自己的编程水平。

通过以上总结和建议,希望能够帮助你更好地学习和掌握C#编程,在实际开发中取得更好的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值