【问题标题】:Change private string from another class从另一个类更改私有字符串
【发布时间】:2012-12-05 19:41:38
【问题描述】:

我想知道为什么当我从继承自 Event 的 Leisure 类访问属性时,我的 Events 类中的私有变量“name”不会改变。我需要 Leisure 使用属性来更改它,然后在我的表单类中,它应该能够从事件中读取“名称”的值。见下文:

public partial class Form1 : Form //Main form class
{
    private string eventType; //used for event type selection
    private string formEventName; //used to store selected event name


     private void itemSend_Click(object sender, EventArgs e)
    {
        //encapsulation
        Events myEv = new Events();
        string name=itemInput.Text; 
        myEv.myEvent(eventType, name);
        formEventName = myEv.myName;
        txtOutput.Text = "Event name is " + formEventName + "\r\n";            
    }

class Events:Form1
{
    private string name; //private variable for event name      
     public string myName //used to change property value depending on what eveny type/ event name
    {
        get { return name; }
        set { name = value; }   
    }
    public void myEvent(string eventType, string eventName) //variable recieved from main form class
    {
        if (eventType == "Leisure")
        {

           Leisure myLes = new Leisure(); 
           myLes.eventNames(eventName);  

        }
        else
        {
            //test for other event types
        }  
    }

    class Leisure:Events
 {
    public void eventNames(string eventName) 
    {

        //when adding new items add one with a capital and one without
        myEventNames.Add("music");
        myEventNames.Add("Music");
        if (myEventNames.Contains(eventName))
        {
            myName = eventName;
        }
        else
        {
            MessageBox.Show("item not found, please try again"); //error message
        }
    }
  }

【问题讨论】:

  • myEventNames 来自哪里?
  • myEventNames 是类中的一个列表,抱歉我必须忘记复制那部分代码
  • 老实说,高水平的设计让我很困扰。这些形式几乎肯定不应该相互继承。您让自己感到困惑,因为您认为您可以访问所有父母的方法,但实际上您并没有与父母互动,因为没有在适当的实例上调用这些方法。继承不是这些表单进行交流的正确方式。
  • 为什么是EventsLeasure 类型的表单?据我所知,它们根本不应该是表单,因为它们没有显示出来,也没有与消息框之外的 UI 元素进行交互。这两个类的真正目的是什么?他们在高层次上试图完成什么?
  • 活动和休闲只是类。事件应该能够访问来自休闲类的信息,然后将这些信息返回到表单中

标签: c# forms inheritance properties private


【解决方案1】:

Events 继承自 Form1 似乎是错误的。

当您说new Events() 时,您将获得一个与现有表单无关的新对象,并且您对其所做的任何更改都不会影响现有表单。当你说 new Leisure() 时,这种情况再次发生。

【讨论】:

    【解决方案2】:

    您使用的myName 属性更改了myLes 实例的name 私有字段,而不是在ItemSend_Click 中创建的myEv 实例的名称私有字段。

    在面向对象语言中,当您创建类的实例时,该实例具有该类中声明的每个非静态私有/公共变量的副本。所以当你写

       Leisure myLes = new Leisure(); 
    

    您正在创建 Leisure 类的实例,但该实例虽然从 Events 继承,但具有不同的内部变量集,并且与当前 Event 实例 (myEv) 的变量不同。

    查看您的代码,我建议创建一个名为

    的第三个类
    public class EventFactory
    {
        public Event CreateEvent(string eventType, string eventName)
        {
            switch(enventType)
            {
                case "Leisure":
                     Leisure myLes = new Leisure(); 
                     myLes.eventNames(eventName);  
                     return myLes;
                // case Add other specialized events here:
                // break;
                default:
                     return null;
            }
        }
    }
    

    更改您的 Events 类,删除 Form1 的继承(据我所知不需要)和方法 myEvent

    现在您的 ItemSend_Click 可以这样写

    private void itemSend_Click(object sender, EventArgs e)
    {
        Events myEv = new EventFactory().CreateEvent(eventType, itemInput.Text);
        formEventName = myEv.myName;
        txtOutput.Text = "Event name is " + formEventName + "\r\n";            
    }
    

    这是可行的,因为Leisure 派生自Events,您可以将每个 Leisure 实例视为一个 Event 实例。

    【讨论】:

    • 这种情况下,如何在当前Event实例中创建休闲类的对象。有没有办法在不使用“新”运算符的情况下做到这一点?
    【解决方案3】:

    您只需在 myEv 字段中更改 myLes (Leisure) 变量的 myName,这就是 myEv.myName 仍然为空的原因。

    【讨论】:

      【解决方案4】:

      因此,您遇到的问题只是代码中主要基本问题的一个小症状,这些问题只会随着您的继续而继续显现。

      我已将您所拥有的内容重新编写成更符合您想要做的事情的传统看法。这并不完美,我尽量让事情变得相当简单,以免一次给你太多麻烦。

      public partial class Form1 : Form //Main form class
      {
          private TextBox itemInput;
          private TextBox txtOutput;
          private string eventType; //used for event type selection
          private string formEventName; //used to store selected event name
      
          private void itemSend_Click(object sender, EventArgs e)
          {
              string name = itemInput.Text;
              try
              {
                  Event myEvent = Event.Create(eventType, name);
                  txtOutput.Text = "Event name is " + myEvent.Name + "\r\n";
              }
              catch (ArgumentException ex)//if the event name isn't valid
              {
                  MessageBox.Show(ex.Message);
              }
          }
      }
      
      public abstract class Event
      {
          public string Name { get; private set; }
          public Event(string eventName)
          {
              Name = eventName;
          }
          public static Event Create(string eventType, string eventName)
          {
              if (eventType == "Leisure")
              {
      
                  Leisure myLes = new Leisure(eventName);
                  return myLes;
      
              }
              //  else if { ... } test for other event types
              else
              {
                  return null;
              }
          }
      }
      
      public class Leisure : Event
      {
          private static List<string> myEventNames =
              new List<string>() { "music", "Music" };
          public Leisure(string eventName)
              : base(eventName)
          {
              if (!myEventNames.Contains(eventName))
              {
                  throw new ArgumentException("Not a valid Leisure event name");
              }
          }
      }
      

      那么,让我们回顾一下其中的一些变化。首先,Event 不继承自 Form1。它不应该这样做。事件在概念上根本不是一种形式,更不用说那种特定类型的形式了。 Event 不应该以任何方式了解任何形式,而不仅仅是继承。它只是Form1 将使用的其他一些类,但它可以很容易地被任何其他类型的类、表单或其他类型使用。

      除了使Event 不继承自Form 之外,我还对其进行了抽象。它没有任何抽象方法,只是你不应该只创建一个普通的Event,你应该只实际创建某种特定类型的事件。作为一个通用基类,最简单的方法是防止意外创建并通过将其设为abstract 来帮助提高可读性。

      我还使Event 不可变。而不是允许随时更改名称,创建一个事件而不给它一个名称,然后再更改它,我已经配置它,你需要在创建事件之前提供名称和类型,然后就没有办法了一旦它被创建就改变它。 Name 是在构造函数中设置的,我添加了一个静态 Create 方法,这是逻辑可以用来选择 Event 的正确子类型并实际创建它的地方。这是“工厂模式”的简单版本。请注意,通常我不会将类型作为字符串传递。我会将其设置为 Enum,以便更容易判断有效选项是什么。

      现在转到Leisure。从逻辑上讲,Leisure 确实是一个Event,应该继承自它。您的问题源于您创建了Event 的实例和Leisure 的实例,并假设它们共享相同的变量。他们没有,但既然你永远不能拥有Event 的实例,那么这种困惑应该会消失。

      创建Leisure 时,它使用基类构造函数设置Name,因为它无权设置属性本身。

      据我所知,myEventNames 只是一个有效名称列表,它似乎在不同类型的Leisure 实例之间没有变化,所以它是static 是有意义的,这意味着它在所有实例之间共享,并且只创建一次。

      我还将MessageBox 调用移出Leisure 类型的构造函数。相反,我抛出了一个异常。这里的主要思想是您不应该将您的 UI 代码与您的业务代码混合在一起。 EventLeisure 都是业务对象,不应该知道 UI 存在什么(如果有的话)。您应该能够从控制台应用程序、ASP 应用程序等中使用它们。最重要的是,因为我们要说的是这是一个无效的名称并且该类型不应该存在,所以最终结果是在构造函数中抛出 Excpetion 是该对象永远不会变为“有效”。我们不允许创建不应该存在的对象,而是允许他们继续使用该对象。

      该异常被Form1 中的try/catch 块捕获,它根据创建事件的失败显示适当的MessageBox

      【讨论】:

      • 非常感谢,这真的很有帮助!
      猜你喜欢
      • 1970-01-01
      • 2011-12-10
      • 2022-01-10
      • 1970-01-01
      • 2014-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-13
      相关资源
      最近更新 更多