C#中方法参数可以使用ref关键字来修饰,这样,在被调用方法中修改的变量其实就是调用者过程中声明的变量,例如:
using System;
public class TestRef
{
    public void ClearString(ref string sourcestring)
    {
         sourcestring="";
    }
    
    private string string1="Hello World!";
    public static void Main()
    {
         System.Console.WriteLine(this.string1);
         this.ClearString(ref string1);
         System.Console.WriteLine(this.string1);
    }
}
在该例子中,方法ClearString通过ref修饰符接受参数并执行后,调用过程中的string1变量就被改变了。这是ref修饰符最简单的用法。我的疑问是,既然被调用方法和调用者所处理的对象是同一个对象,而且在被调用方法中的修改会对调用者过程产生影响,那么,如果在调用者过程中修改该变量,也应该会同时影响被调用方法中该变量的值。例如:using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;namespace TestEvent
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private string text; private System.Windows.Forms.TextBox textBox1;
private TestEvent.SmartText textBox2;
private TestEvent.SmartText textBox3;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null; public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent(); //
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
} /// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null) 
{
components.Dispose();
}
}
base.Dispose( disposing );
} #region Windows Form Designer generated code
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.text=this.textBox1.Text;
this.textBox2 = new TestEvent.SmartText();
this.textBox3 = new TestEvent.SmartText();
this.SuspendLayout();
// 
// textBox1
// 
this.textBox1.Location = new System.Drawing.Point(16, 16);
this.textBox1.Name = "textBox1";
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
// 
// textBox2
// 
this.textBox2.Location = new System.Drawing.Point(16, 56);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(216, 24);
this.textBox2.TabIndex = 1;
this.textBox2.SetText(ref this.text);
// 
// textBox3
// 
this.textBox3.Location = new System.Drawing.Point(16, 96);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(216, 24);
this.textBox3.TabIndex = 2;
this.textBox3.SetText(ref this.text);
// 
// Form1
// 
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
  this.textBox3,
  this.textBox2,
  this.textBox1});
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false); }
#endregion /// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main() 
{
Application.Run(new Form1());
} private void textBox1_TextChanged(object sender, System.EventArgs e)
{
this.text=this.textBox1.Text;
this.textBox2.SetText(ref this.text);
this.textBox3.SetText(ref this.text);
}
}
}以上是调用者代码,是一个窗体,上面有三个TextBox控件,其中textBox1是标准TextBox控件,textBox2,textBox3始自定义控件,其代码如下:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;namespace TestEvent
{
public class TextChangeEventArgs:System.EventArgs
{
private string _text;
public string Text
{
get
{
return _text;
}
set
{
_text=value;
}
}
} public delegate void TextChangeEventHandler(object sender,TextChangeEventArgs tce); /// <summary>
/// SmartText 的摘要说明。
/// </summary>
public class SmartText : System.Windows.Forms.UserControl
{
public event TextChangeEventHandler ChangeText; protected void OnTextChange(object sender,TextChangeEventArgs tce)
{
if(ChangeText!=null)
{
ChangeText(this,tce);
} this.textBox1.Text=(string)(this._text);
} private object _text;
private System.Threading.Thread th;
private System.Windows.Forms.TextBox textBox1;
/// <summary> 
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null; public SmartText()
{
// 该调用是 Windows.Forms 窗体设计器所必需的。
InitializeComponent();
// TODO: 在 InitializeComponent 调用后添加任何初始化
th=new System.Threading.Thread(new System.Threading.ThreadStart(this.Start));
th.Start(); } public void SetText(ref string Text)
{
this._text=Text;
} private void Start()
{
string staticText=(string)(this._text);
while(true)
{
System.Threading.Thread.Sleep(1000);
if(staticText!=(string)(this._text))
{
TextChangeEventArgs tce=new TextChangeEventArgs();
tce.Text=staticText;
staticText=(string)(this._text);
this.OnTextChange(this,tce);
}
}
}
/// <summary> 
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
} #region Component Designer generated code
/// <summary> 
/// 设计器支持所需的方法 - 不要使用代码编辑器 
/// 修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
// 
// textBox1
// 
this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(216, 21);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
// 
// SmartText
// 
this.Controls.AddRange(new System.Windows.Forms.Control[] {
  this.textBox1});
this.Name = "SmartText";
this.Size = new System.Drawing.Size(216, 24);
this.ResumeLayout(false); }
#endregion
}
}
该自定义控件的功能是,监视指定的字符串变量,当变量发生改变时,触发ChangeText事件,并刷新其显示。该控件一旦被声明并初始化,就启动一个线程,该线程不断比较调用者过程中指定的变量,如果发生改变,就引发ChangeText事件。但上边的程序在执行时,线程对应的方法无法跟踪调用者所指定的变量。这里我就使用了ref修饰符来处理被监视对象。请高手指点一二。

解决方案 »

  1.   

    补充,在调用者代码中,
    private void textBox1_TextChanged(object sender, System.EventArgs e)
    {
             this.text=this.textBox1.Text;
    this.textBox2.SetText(ref this.text);
    this.textBox3.SetText(ref this.text);
    }
    方法中后两行应取掉,否则就不是控件自动监视变量,而是由调用者通知了。但实际运行时,如去掉,就无法监视,通过跟踪,好像被监视的变量根本无法传递到控件的Start过程中.
      

  2.   

    public void SetText(ref string Text)
    {
    this._text=Text;
    }
    这句我看不懂,为什么需要ref 程序内部并不修改Text的值。System.Threading.Thread.Sleep(1000);也不太好吧?
      

  3.   

    gujunyan(ivy):
        谢谢你对我的问题的关注。
        我自定义的控件的功能是监视调用者过程中的特定变量的值是否变化,因此,它不能对这个变量进行修改,所以在自定义控件的SetText方法中,我将类内部的临时对象指向引用的Text对象。然后在Start()方法中,通过设立临时变量,来不断的获取Text对象的值和临时变量比较,如果不一样,就触发事件,同时刷新临时变量。但通过跟踪程序,发现获取的Text对象的值只是控件初始化时通过SetText方法获得的值,此后,Start()方法内的_text再也没有更新了,这也是我所疑惑的地方。
        至于Thread.Sleep(1000),只是随便设了一下,并无实质性影响。
      

  4.   

    对该问题的补充说明
        实际上该问题简单来讲,就是使用ref修饰符传递参数,可以在被调用方法内对调用者的变量进行修改,并影响调用者对该变量的使用。那么反过来,在使用了ref修饰符的时候,如果在被调用的方法执行过程中(比如该过程启动一个线程,不断检测传递进来的参数),该参数发生了改变,是否能够在被调用方法中检测到?
        在C++中,可以直接传递要监视的参数的指针给这个线程,但在C#中该如何办到?(不使用unsafe代码)
      

  5.   

    有两个建议,首先,对于内部不需要修改外部传入参数内容时,建议不要使用ref关键字。
    第二,字符串比较,不要使用==或!=来进行判断。应该使用Equals()来进行判断。我对您这段代码的理解是, 想让自己做的组件可以捕获到内容是否有变化。并从OnTextChange事件中得到变化的结果。
    我认为,使用另一个线程来定时判断,不太好。资源浪费不错,反而会转难控制。
    正常情况下,TextBox的TextChanged是一个个字符级的响应的,若要做到输完一句后一次性检查,可以使用Validated
    或Validating来处理。关于这方面的内容,请看帮助。
    操作很容易。