如何使用实用程序类中的属性设置器的静态实用程序方法 [英] How to use a static utility method for property setters in a utility class

查看:294
本文介绍了如何使用实用程序类中的属性设置器的静态实用程序方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在DataGridView和提供DGV数据的BindingList之间实现双向绑定。一些列还没有反映底层列表中的更改,我认为这是因为我没有提供属性设置器来通知属性更改。而不是像Rows属性一样为Process属性编写setter,而是试图获得更多的优雅,我意识到我被卡住了....


$ b $我绊倒了一个非常有趣的写作,更优雅的方法,我试图实现它的概念(请参阅):
http://www.gavaghan.org/blog/2007/07/17/use-inotifypropertychanged-with-bindinglist/



以下是我要使用的Mike的文章(在我的CBMI.Common项目中建立为 Utilities.cs )的代码, :

  using System; 
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Linq;
使用System.Text;
命名空间CBMI.Common
{
public static class Utilities
{
public static bool Set< T>(对象所有者,string propName,
ref T oldValue,T newValue,PropertyChangedEventHandler eventHandler)
{
//确保属性名称真正存在
if(owner.GetType()。GetProperty(propName)== null)
{
throw new ArgumentException(No property named'+ propName +on on+ owner.GetType()。FullName);
}
if(!Equals(oldValue,newValue))//如果值更改,我们只会引发一个事件
{
oldValue = newValue;
if(eventHandler!= null)
{
eventHandler(owner,new PropertyChangedEventArgs(propName));
}
}
返回true; //请注意:我不得不添加这个语句来避免编译错误:
//并不是所有的代码路径都返回一个值。
}
}
}

所以,我的第一个问题:作者在他的文章中没有一个返回语句,我添加了它解决了编译器错误。我猜测eventHandler执行并返回,这是一个作者省略,这应该返回true,因为该方法需要一个bool返回类型。这是正确的假设吗?



我的第二个问题显示了当我尝试使用上述帮助方法时,C#菜鸟是什么。我将这个类编码到与上述相同的项目(和命名空间)中的一个名为 InputFileInfo.cs 的单独文件:

 使用系统; 
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Linq;
使用System.Text;
命名空间CBMI.Common
{
public class InputFileInfo:INotifyPropertyChanged
{
private bool processThisFile;
public bool Process
{
get {return processThisFile; }
set
{
processThisFile = value;
this.NotifyPropertyChanged(Process);
}
}

public string FileName {get;组; }

private long rowsReturned;
public long Rows
{
get {return rowsReturned; }
set
{
Utilities.Set(this,Rows,ref rowsReturned,value,PropertyChanged);
}

}
public string Message {get;组;

//构造函数
public InputFileInfo(string fName)
{
Process = true;
FileName = fName;
Rows = 0;
Message = String.Empty;
}

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string name)
{
if(PropertyChanged!= null)
PropertyChanged(this,new PropertyChangedEventArgs(name));
}
}
}

第二个属性的setter在这个类中,我尝试使用Mike的静态方法:

  Utilities.Set(this,Rows,ref rowsReturned, value,PropertyChanged); 

如果我删除Utilities.Set,只需要编码如下:

  Set(this,Rows,ref rowsReturned,value,PropertyChanged); 

然后我得到编译器抱怨名称Set不存在在当前上下文中。



我尝试使用实用程序添加指令,但这并没有解决问题。最后,我不明白参数:ref T oldValue,T newValue

和参数叫做,其中调用Set方法。



有人可以帮助我解决这些关于这段代码的多重混淆,以便我可以使用这些更先进的想法?



----编辑更新----
两个好的答案帮助我得到这个工作。上述原始帖子中的第二个问题仍然有点难以捉摸。为每个请求一个关于如何打包这个的最佳实践的评论添加了评论,所以我可以使用简单的调用语法,如迈克的原始文章。也就是说,我正在通过方法名称调用帮助静态方法。我想了解如何调用:

  set 
{
Set(this,Rows ,ref rowsReturned,value,PropertyChanged);
}

而不必编码为:

  set 
{
Utilities.Set(this,Rows,ref rowsReturned,value,PropertyChanged);
}

我通过编码Utilities.Set这个工作,但我猜这个问题变形位于 - 我在哪里放置静态方法和如何调用它们,所以我不必使用classname限定他们? 我想了解如何打包一般有用的实用程序类型的方法,不需要一个对象的实例。在这种情况下,静态方法称为Set,但我想要添加其他静态方法,如:

  public static int HelpMethodXXXX(string s,int num)

我有一个单独编译的DLL(Vstudio项目)只包含类文件。最终,我想认为我可以在其他应用程序中使用这个类。



最好的地方是声明这些静态方法,以便可以调用它们:

  int i = UsefulMethodXXXX(Sample,testNumber); 

而不是:

  int i = ContainingClassName.HelpfulMethodXXXX(Sample,testNumber); 


解决方案

1:所有非void方法都需要有明确的返回语句。



2:CMBI.Common是命名空间。实用工具是你的班级的名字。 Set()是你的类的一个函数。



只有在Utilities类的上下文中调用Set()才有意义。 Set()不是全局命名空间的一部分 - 因此,如果要在Utilities类之外调用Set(),则必须指定您想要的Utilities.Set(),而不是SomethingElse.Set() 。 (Inside Utilities,编译器了解Set()指的是Utilities.Set())。



使用语句只能包含命名空间(CMBI.Common)或特定类如果您不希望命名空间中的每个类(CMBI.Common.Utilities)都可以使用命名空间。但是,它们不能将类函数转换为全局函数。



3:T表示此函数运行的泛型类型的名称。 http://msdn.microsoft.com/en- us / library / ms379564(v = vs80).aspx



泛型允许相同的代码来操纵一个整数的集合,收集字符串,同时强制执行编译时类型的安全性(编译器将给出错误,如果您尝试将整数推入字符串集合。)



ref表示该参数作为参考传递,并且对函数体内的参数所做的更改将在函数调用者的上下文中传播到参数的值。


I'm trying to achieve two-way binding between a DataGridView and a BindingList that provides data for the DGV. Some columns do not yet reflect changes in the underlying list and I think it's because I have not provided property setter(s) to notify of property changes. Rather than code the setter for the Rows property the same way I did for the Process property, I'm trying to get more "elegant" and I realize I am stuck....

I stumbled upon a very interesting writeup for a more elegant approach and I'm trying to implement the concepts of it (please see): http://www.gavaghan.org/blog/2007/07/17/use-inotifypropertychanged-with-bindinglist/

Here is the code from Mike's article I want to use (established as Utilities.cs in my CBMI.Common project):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace CBMI.Common
{
public static class Utilities
{
    public static bool Set<T>(object owner, string propName,
        ref T oldValue, T newValue, PropertyChangedEventHandler eventHandler)
    {
        // make sure the property name really exists
        if (owner.GetType().GetProperty(propName) == null)
        {
            throw new ArgumentException("No property named '" + propName + "' on " + owner.GetType().FullName);
        }
        if (!Equals(oldValue, newValue))  // we only raise an event if the value has changed
        {
            oldValue = newValue;
            if (eventHandler != null)
            {
                eventHandler(owner, new PropertyChangedEventArgs(propName));
            }
        }
    return true;    // Please NOTE: I had to add this statement to avoid compile error:
        // "not all code paths return a value".
    }
}
}

So, my FIRST QUESTION about this: The author did not have a return statement in his article and I added it which resolved the compiler error. I'm guessing the eventHandler executes and returns and that this was an author omission and this should return true as the method wants a bool return type. Is that correct assumption?

My 2nd QUESTION shows what a C# rookie I am when I try to use this helper method above. I have coded this class into a separate file called InputFileInfo.cs in the same project (and namespace) as the above:

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace CBMI.Common
{
    public class InputFileInfo : INotifyPropertyChanged
    {
    private bool processThisFile;
    public bool Process
    {
        get { return processThisFile; }
        set
        {
        processThisFile = value;
        this.NotifyPropertyChanged("Process");
        }
    }

    public string FileName { get; set; }

    private long rowsReturned;
    public long Rows
    {
        get { return rowsReturned; }
        set
        {
        Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
        }

    }
    public string Message { get; set; } 

    // constructor
    public InputFileInfo(string fName)
    {
        Process = true;
        FileName = fName;
        Rows = 0;
        Message = String.Empty;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    }
}

The setter for the 2nd property in this class is where I try to use Mike's static method:

Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);

If I remove Utilities.Set and just code it as follows:

Set(this, "Rows", ref rowsReturned, value, PropertyChanged);

..then I get the compiler complaining that "the name 'Set' does not exist in the current context".

I tried adding a using Utilities; directive but that did not fix the problem.

Finally, I do not understand the parameters: ref T oldValue, T newValue
nor the parameter called value where the Set method is invoked.

Can someone please help me over these multiple confusions about this code so I can use these more advanced ideas?

---- EDIT UPDATE ---- Two good answers helped me get this working. The "2nd question" in the original post above remains a bit elusive. Added comments for each requesting a "best practice" on how to package this so I can use the simple invoking syntax as in Mike's original article. That is, I'm seeking to invoke "helper" static methods by the method name only. I want to understand how to invoke like:

set
{
    Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
}

instead of having to code as:

set
{
    Utilities.Set(this, "Rows", ref rowsReturned, value, PropertyChanged);
}

I got this working by coding Utilities.Set but I guess the question morphs a bit into - "Where do I put static methods and how to call them so I don't have to "qualify" them with the classname?" I would like to understand how to package generally useful "utility" type methods that don't require an instance of an object. In this case, the static method is called Set but I'd like to be able to add other static methods such as:

public static int HelpfulMethodXXXX(string s, int num)

I have a separately compiled DLL (Vstudio project) containing only class file(s). Ultimately, I'd like to think I could use this class in other applications.

Where is the best place to declare these sort of static methods such that they could be invoked as:

int i = HelpfulMethodXXXX("Sample", testNumber);

instead of:

int i = ContainingClassName.HelpfulMethodXXXX("Sample", testNumber);

解决方案

1: All non-void methods need to have explicit return statements.

2: CMBI.Common is the namespace. Utilities is the name of your class. Set() is a function of your class.

The call to Set() only makes sense in the context of the Utilities class. Set() is not part of the global namespace - as such, if you want to call Set() outside of the Utilities class, you have to specify that you want the Utilities.Set(), as opposed to SomethingElse.Set(). (Inside Utilities, the compiler understands that Set() refers to Utilities.Set()).

Using statements can only include namespaces (CMBI.Common), or specific classes inside namespaces, if you don't want every class in the namespace (CMBI.Common.Utilities). They cannot, however, turn a class function into a global function.

3: T refers to the name of the Generic type that this function operates on. http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx

Generics allow the same code to manipulate, say, a collection of integers, and a collection of strings, while enforcing compile-time type safety (The compiler will give an error, if you try to push an integer into a collection of strings.)

ref means that the parameter is passed as a reference - and that changes made to the parameter within the body of a function will propagate to the parameter's value in the context of the function's caller.

这篇关于如何使用实用程序类中的属性设置器的静态实用程序方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆