【问题标题】:Choosing to not use delegates in a C# program选择不在 C# 程序中使用委托
【发布时间】:2014-02-12 23:18:49
【问题描述】:

我正在尝试理解 C# 中的委托。我知道它们在 linq 等许多场景中是必须的,但是在简单的控制台应用程序中,如果我选择不使用它,我的生活会如此悲惨吗?我不是想逃避它或任何东西。我只是想权衡使用它与不使用它的重要性。例如,我从某个地方抓取了一个 c# 控制台示例。我将如何着手将这个使用委托的优秀程序解构为一个不使用委托的所谓不优雅的版本?

class Program
{
    // Define our delegate type: pointer to any method taking in string parameter and returning void
    public delegate void Write(string theText);

    // Method for output to screen
    public static void OutputToScreen(string theText)
    {
        Console.WriteLine(theText);
    }

    // Method to output to file
    public static void WriteToFile(string theText)
    {
        StreamWriter fileWriter = File.CreateText("delegatedemo.txt");
        fileWriter.WriteLine(theText);
        fileWriter.Flush();
        fileWriter.Close();
    }

    public static void Main()
    {
        // Assign a method to a delegate
        Write toFile = new Write(WriteToFile);
        Write toScreen = new Write(OutputToScreen);

        Display("This is a delegate demo", toFile);
        Display("This is a delegate demo", toScreen);
    }

    public static void Display(string theText, Write outputMethod)
    {
        outputMethod(theText);
    }
}

【问题讨论】:

  • 理解代表需要时间。早在过去,计算机科学 101 班会在学科出现指针时失去一半的学生。一个额外的间接级别,需要时间来理解。代表并不像指针那样不透明,所以不要惊慌。继续尝试使用它们,你会得到啊!片刻。没有人可以真正为您加快速度。

标签: c# linq events methods delegates


【解决方案1】:

让我们从基础开始。委托本质上是类型安全的函数指针。因此,某个委托类型的变量用于“指向”其签名与该委托类型兼容的方法。由于函数指针在实际调用所述函数之前不会做任何有用的事情,因此它们的吸引力在于允许您选择要在运行时调用的函数。

没有委托的简单版本将涉及摆脱可变部分并仅对函数调用进行硬编码。当然,生成的程序在功能上不可能是等效的,因为它缺乏委托提供的灵活性,但它仍然可以产生任何一种特定的结果,而使用委托的版本可以:

public static void Main()
{
    // Same runtime effect, but less flexibility: the effects are hardcoded
    WriteToFile("This is not a delegate demo");
    OutputToScreen("This is not a delegate demo");
}

可以通过使用switch 语句或任何其他类似构造将委托替换为某种标量类型并在稍后将值的解析推迟到调用的方法来实现功能等效的版本。但是,此版本使用起来会更加麻烦,并且可能会犯下只能在运行时检测到的错误。

public static void Main()
{
    DoSomethingWith("This is not a delegate demo", "display");
    DoSomethingWith("This is not a delegate demo", "write");
}

static void DoSomethingWith(string str, string what)
{
    // Same runtime effect and flexibility, but...
    switch (what) {
        case "display": 
            OutputToScreen(str); break;
        case "write:
            WriteToFile(str); break;
        // what happens if "what" is neither of the above?
    }
}

【讨论】:

  • 第一段的解释真棒!非常感谢您花时间向我解释这个概念!
【解决方案2】:

这很简单。只需删除委托本身,删除Display 方法,从Main 中删除toFiletoScreen 变量,并将Main 中的Display 调用替换为对WriteToFileOutputToScreen 的调用:

class Program
{
    // Method for output to screen
    public static void OutputToScreen(string theText)
    {
        Console.WriteLine(theText);
    }

    // Method to output to file
    public static void WriteToFile(string theText)
    {
        StreamWriter fileWriter = File.CreateText("theFile.txt");
        fileWriter.WriteLine(theText);
        fileWriter.Flush();
        fileWriter.Close();
    }

    public static void Main()
    {
        WriteToFile("This is not a delegate demo");
        OutputToScreen("This is not a delegate demo");
    }
}

你也可以去掉 OutputToScreen 方法:

class Program
{
    // Method to output to file
    public static void WriteToFile(string theText)
    {
        StreamWriter fileWriter = File.CreateText("theFile.txt");
        fileWriter.WriteLine(theText);
        fileWriter.Flush();
        fileWriter.Close();
    }

    public static void Main()
    {
        WriteToFile("This is not a delegate demo");
        Console.WriteLine("This is not a delegate demo");
    }
}

【讨论】:

    【解决方案3】:

    委托可以被认为是一个指向函数的对象。

    如果您不想使用委托,可以将 Display 函数更改为使用 int 参数或枚举,并执行 if 语句来决定调用哪个函数。

    int 示例:

    class Program
    {
    
        // Method for output to screen
        public static void OutputToScreen(string theText)
        {
            Console.WriteLine(theText);
        }
    
        // Method to output to file
        public static void WriteToFile(string theText)
        {
            StreamWriter fileWriter = File.CreateText("delegatedemo.txt");
            fileWriter.WriteLine(theText);
            fileWriter.Flush();
            fileWriter.Close();
        }
    
        public static void Main()
        {
            Display("This is a delegate demo", 1);
            Display("This is a delegate demo", 2);
        }
    
        public static void Display(string theText, int type)
        {
            if( type == 1)
                WriteToFile(theText);
            else if( type == 1)
                OutputToScreen(theText);
        }
    }
    

    【讨论】:

      【解决方案4】:

      了解为什么您要删除代表可能会有所帮助。唯一的原因是因为你不熟悉它们吗?或者您是否试图从哲学上理解何时何处它们是有用的?

      不管怎样,在这个短节目中,有很多方法可以去除代表。在这种情况下,您可能会使用情况指示器(枚举或 int)在要调用的函数之间切换。这是与原始意图/结构略有不同的一种可能性:

      class Program {
          enum WriteType { ToScreen, ToFile };
      
          // Method for output to screen
          public static void OutputToScreen(string theText) {
              Console.WriteLine(theText);
          }
      
          // Method to output to file
          public static void WriteToFile(string theText) {
              StreamWriter fileWriter = File.CreateText("nondelegatedemo.txt");
              fileWriter.WriteLine(theText);
              fileWriter.Flush();
              fileWriter.Close();
          }
      
          public static void Main() {
              Display("This is a non-delegate demo", WriteType.ToFile);
              Display("This is a non-delegate demo", WriteType.ToScreen);
          }
      
          public static void Display(string theText, WriteType outputMethod) {
              switch (outputMethod) {
              case WriteType.ToFile: WriteToFile(theText); break;
              case WriteType.ToScreen: WriteToScreen(theText); break;
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        可能有很多方法可以在没有委托的情况下执行此操作,但我假设您希望以类似的方式调用任何类型的输出。我认为接口和委托一样可以解决这个问题:

        interface IWriter {
            public static void Write(string text);
        }
        class FileWriter : IWriter {
            public void Write(string text)
            {
                StreamWriter fileWriter = File.CreateText("delegatedemo.txt");
                fileWriter.WriteLine(theText);
                fileWriter.Flush();
                fileWriter.Close();
            }
        }
        class ConsoleWriter: IWriter {
            public void Write(string text)
            {
                Console.WriteLine(theText);
            }
        }
        public static void Main()
        {
            new FileWriter.Write("This is a NOT delegate demo");
            new ConsoleWriter.Write("This is NOT a delegate demo");
        }
        

        【讨论】:

          【解决方案6】:

          您的代码可以通过使用预先存在的Action<T> delegate 并消除toFiletoScreen 变量(您不需要)来缩短一点:

          public static void Display(string theText, Action<string> outputMethod)
          {
              outputMethod(theText);
          }
          

          然后这样称呼它:

          Display("Hello World", WriteToFile);
          

          Display("Hello World", OutputToScreen);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-11-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-04-04
            • 2023-04-07
            相关资源
            最近更新 更多