【问题标题】:How to use a static utility method for property setters in a utility class如何为实用程序类中的属性设置器使用静态实用程序方法
【发布时间】:2011-10-09 19:04:53
【问题描述】:

我正在尝试在 DataGridView 和为 DGV 提供数据的 BindingList 之间实现双向绑定。有些列尚未反映基础列表中的更改,我认为这是因为我没有提供属性设置器来通知属性更改。我没有像为 Process 属性那样为 Rows 属性编写 setter 代码,而是试图变得更“优雅”,但我意识到我被困住了......

我偶然发现了一篇非常有趣的文章,提出了一种更优雅的方法,我正在尝试实现它的概念(请参阅): http://www.gavaghan.org/blog/2007/07/17/use-inotifypropertychanged-with-bindinglist/

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

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".
    }
}
}

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

我的 2nd QUESTION 显示了当我尝试使用上面的这个辅助方法时,我是一个 C# 菜鸟。我已将此类编码到与上述相同的项目(和命名空间)中名为 InputFileInfo.cs 的单独文件中:

    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));
    }
    }
}

这个类中第二个属性的设置器是我尝试使用 Mike 的静态方法的地方:

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

如果我删除 Utilities.Set 并将其编码如下:

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

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

我尝试添加一个 using Utilities;指令,但这并没有解决问题。

最后看不懂参数:ref T oldValue, T newValue
也不是调用 Set 方法的名为 value 的参数。

有人可以帮我解决有关此代码的多重困惑,以便我可以使用这些更高级的想法吗?

---- 编辑更新---- 两个好的答案帮助我完成了这项工作。上面原始帖子中的“第二个问题”仍然有点难以捉摸。为每个请求如何打包它的“最佳实践”添加了 cmets,以便我可以使用 Mike 原始文章中的简单调用语法。也就是说,我正在寻求仅通过方法名称来调用“帮助”静态方法。我想了解如何调用:

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

而不必编码为:

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

我通过编写 Utilities.Set 来完成这项工作,但我猜这个问题有点变形 - “我在哪里放置静态方法以及如何调用它们,所以我不必“限定”它们与类名?” 我想了解如何打包不需要对象实例的通常有用的“实用程序”类型方法。在这种情况下,静态方法称为 Set 但我希望能够添加其他静态方法,例如:

public static int HelpfulMethodXXXX(string s, int num)

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

声明这些静态方法以便它们可以被调用的最佳位置是:

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

代替:

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

【问题讨论】:

    标签: c# datagridview inotifypropertychanged bindinglist system.componentmodel


    【解决方案1】:
    • 看起来应该将返回类型从 bool 更改为 void,因为它似乎永远不会返回不同的值。

    • 是的,Utilities.Set 是正确的语法。例如,Java 中没有静态导入的类似物,因此您必须使用类对其进行限定。如果您愿意,可以使用扩展方法,并且能够调用像 this.Set(...) 这样的方法。为此,只需在 Set 方法的第一个参数前添加 this 关键字:

     

    public static bool Set<T>(this object owner, string propName,
        ref T oldValue, T newValue, PropertyChangedEventHandler eventHandler)
    
    • ref T oldValue 表示您可以将T 类型的变量传递给它,它会将旧值写入其中(换句话说,值类型的传递引用)。这样您就可以找出oldValue 是什么。 (虽然在我看来out 参数会更有意义。)

    • T newValue 是您尝试设置的新值。如果您要问T 是什么,它是一个泛型类型,并充当 really 类型的占位符。它会根据您传递给它的参数类型自动确定哪种类型。 (如果你传递一个string,T 的行为就像你使用string 而不是T。)

    • value 是 C# 中的一个特殊关键字,仅在您定义的属性的 set 访问器中具有特殊含义。这是您尝试分配给属性的值。

    【讨论】:

    • 感谢柯克的大力帮助。我将返回类型更改为 void。我可以看到 Utilities.Set 是如何有效的,但我试图遵循文章作者 (Mike) 的指导。 “扩展方法”和语法更改似乎比实现 Mike 的 Set 方法更“优雅”。您能否提出一个“最佳实践”,将我的课程打包在一个文件中,而不是每个课程一个文件(请参阅我对 Vladislav 答案的评论)。最后,感谢我在此处尝试对泛型的简洁解释。很有帮助。
    【解决方案2】:

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

    2:CMBI.Common 是命名空间。实用程序是您的类的名称。 Set() 是你的类的一个函数。

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

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

    3:T 指的是该函数操作的通用类型的名称。 http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx

    泛型允许相同的代码操作整数集合和字符串集合,同时强制编译时类型安全(如果您尝试将整数推送到字符串。)

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

    【讨论】:

    • 谢谢弗拉迪斯拉夫。 (1)我删除了return语句并将方法更改为return void(我想这只是我试图借用代码的作者的疏忽)。 (3) 感谢与我的问题 (2) 最佳实践相关的泛型有用总结:我有一个名为 CBMI.Common 的项目,其中包含 3 个单独的文件(Utilities.cs、InputFileInfo.cs、DocMetaData.cs)。每个文件只定义一个公共类(类名与文件名相同)。您的解释向我表明,最好只有一个文件 - biz.cs - 包含所有类。请发表评论。
    • 另外,如果您在一个团队中工作,如果两个开发人员签出了一个大文件,即使他们的更改不会导致冲突,他们也必须合并才能提交他们的更改。处理多个文件也更容易,然后处理一个超级文件。
    • 你在这里提出了很好的观点。我每天都在学习更多。您是否碰巧注意到我对上述 OP 的 EDIT-UPDATE ?我的程序现在正在运行,但我仍然无法将 Utilities.Set 调用为 Set。我可以忍受它,但在编辑更新中我试图概括这个问题。我现在正在检查这个作为答案,但如果你想再次权衡一下,我会很感激的。为你干杯!
    • 与 C++ 或 C 不同,您不能将函数添加到全局命名空间,无论它是否是静态的。所有函数都属于它们的类,如果你想在它们的类之外调用它们,必须用它们的类名来量化。您不应该尝试解决这个问题 - 它是对 C++ 或 C 的改进,因为将其他开发人员的代码添加到您的项目中不会导致全局命名空间中的函数名称冲突。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    相关资源
    最近更新 更多