5

假设我有

public delegate DataSet AutoCompleteDelegate(
      string filter, long rowOffset);

我可以制作以下课程来强制执行该方法签名吗?(只是一个想到的想法):

public class MiddleTier
{
    [Follow(AutoCompleteDelegate)]
    public DataSet Customer_AutoComplete(string filter, long rowOffset)
    {
        var c = Connect();
        // some code here
    }

    [Follow(AutoCompleteDelegate)]
    public DataSet Item_AutoComplete(string filter, long rowOffset)
    {
        var c = Connect();
        // some code here
    }



    // this should give compilation error, doesn't follow method signature
    [Follow(AutoCompleteDelegate)]
    public DataSet BranchOffice_AutoComplete(string filter, string rowOffset)
    {
        var c = Connect();
        // some code here
    }         

}

[编辑]

目的:我已经在中间层的方法中添加了属性。我有这样的方法:

public abstract class MiddleTier : MarshalByRefObject
{
    // Operation.Save is just an enum

    [Task("Invoice", Operation.Save)]
    public Invoice_Save(object pk, DataSet delta);

    [Task("Receipt", Operation.Save)]
    public Receipt_Save(object pk, DataSet delta);


    // compiler cannot flag if someone deviates from team's standard
    [Task("Receipt", Operation.Save)]
    public Receipt_Save(object pk, object[] delta); 
}

然后在运行时,我将迭代所有中间层的方法并将它们放入集合(属性在这里很有帮助),然后将它们映射到 winform 的委托函数(由接口、基于插件的系统促进)加载

我正在考虑是否可以使属性更具自描述性,以便编译器可以捕获不一致。

namespace Craft
{        
    // public delegate DataSet SaveDelegate(object pk, DataSet delta); // defined in TaskAttribute

    public abstract class MiddleTier : MarshalByRefObject
    {

        [Task("Invoice", SaveDelegate)]        
        public abstract Invoice_Save(object pk, DataSet delta);

        [Task("Receipt", SaveDelegate)]
        // it's nice if the compiler can flag an error
        public abstract Receipt_Save(object pk, object[] delta);
    }
}

我在想,如果将方法放在每个类上,总是实例化一个 Remoting 对象将是一种矫枉过正的做法。并将它们放在单独的类中,可能更难促进代码重用,假设 Invoice_Save 需要一些关于 Receipt_Open 的信息。事实上我什至这里有一个报告(水晶),它从远程中间层数据集中获取数据,在调用的方法内部,它获取其他方法的一些信息并合并到自己的数据集中,它们都发生在中间层,没有几次往返,一切都在服务器端(中间层)完成

4

6 回答 6

11

其他答案显然是有效的,但没有什么可以防止您忘记[Follow(AutoCompleteDelegate)]在您的方法上应用属性。

我认为您最好将方法转换为实现接口的类:

public interface IAutoComplete
{
    DataSet Complete(string filter, long rowOffset);
}

public class CustomerAutoComplele : IAutoComplete
{
    public DataSet Complete(string filter, long rowOffset)
    {
        var c = Connect();
        // some code here
    }
}

然后使用工厂方法模式来获取您的“自动完成器”:

public static class AutoCompleteFactory
{
    public static IAutoComplete CreateFor(string purpose)
    {
        // build up and return an IAutoComplete implementation based on purpose.
    }
}

或者

public static class AutoCompleteFactory
{
    public static IAutoComplete CreateFor<T>()
    {
        // build up and return an IAutoComplete implementation based on T which
        // could be Customer, Item, BranchOffice class.
    }
}

一旦你有了这些,你可以看看控制反转和依赖注入,以避免在你的工厂方法中对自动完成实现列表进行硬编码。

于 2009-06-29T07:24:24.483 回答
3

您可以实现FollowAttribute您在示例中使用的两种方法,并编写一个静态分析(例如,FxCop)规则,该规则可以检查使用该属性标记的任何方法是否具有与上述委托相同的签名。所以应该是可以的。

于 2009-06-29T07:06:43.537 回答
1

这不是语言功能,但是...

这是您可以进行验证的事情:编写反映类的单元测试,如果签名与属性声明不匹配则失败。

PostSharp还为您提供了一些有趣的选项来围绕编译执行此操作。我不知道你会如何使用它,但我怀疑你可以......

于 2009-06-29T07:07:10.790 回答
1

我会质疑你为什么要这样做。如果您不希望通过继承更改类,则可以将其设为密封类。如果您担心将来有人更改班级,则有两种情况中的一种。1)他们不明白自己在做什么;没有什么能阻止一个糟糕的程序员做坏事,如果他们有完全的权力来编辑程序文本。2) 他们正在以一种您目前不了解的方式扩展类功能,这会损害重用。

于 2009-06-29T15:40:52.080 回答
0

属性在编译期间存储为额外的元信息 - 您可以在运行时查询它们,但在编译期间它们不被考虑在内。

您不能通过其上的属性来限制方法。您可以限制属性的应用方式(即仅在方法上,或者是否可以应用多个)。

我建议使用 FxCop 在属性不匹配时发出警告 - 如果您确实小心事件支持类型转换的方式:

[Follow(AutoCompleteDelegate)]
public DataSet Customer_AutoComplete(string filter, int rowOffset)

将是一个有效的代表。

于 2009-06-29T07:30:00.837 回答
0

不。

有点。

您无法在编译时获得此行为。您可以使用 Attributes 将其放入一个简单的测试工具中,或者在实例化包含类时强制立即失败。

考虑两个(快速破解)属性:

[AttributeUsage(AttributeTargets.Class)]
public class EnforceConforms : Attribute
{
    public EnforceConforms(Type myClass)
        : base()
    {
        MethodInfo[] info = myClass.GetMethods();

        foreach (MethodInfo method in info)
        {
            object[] objs = method.GetCustomAttributes(false);

            foreach (object o in objs)
            {
                Attribute t = (Attribute)o;

                if (t.GetType() != typeof(ConformsAttribute)) continue;

                MethodInfo mustConformTo = ((ConformsAttribute)t).ConformTo;

                ParameterInfo[] info1 = mustConformTo.GetParameters();
                ParameterInfo[] info2 = method.GetParameters();

                bool doesNotCoform = false;

                doesNotCoform |= (mustConformTo.ReturnType != method.ReturnType);
                doesNotCoform |= (info1.Length != info2.Length);

                if (!doesNotCoform)
                {
                    for (int i = 0; i < info1.Length; i++)
                    {
                        ParameterInfo p1 = info1[i];
                        ParameterInfo p2 = info2[i];

                        if (!p1.ParameterType.Equals(p2.ParameterType))
                        {
                            doesNotCoform = true;
                            break;
                        }
                    }
                }

                if (doesNotCoform)
                {
                    throw new Exception(myClass.Name + "." + method.Name + " does not conform to required delegate signature");
                }
            }
        }
    }
}

[AttributeUsage(AttributeTargets.Method)]
public class ConformsAttribute : Attribute
{
    public MethodInfo ConformTo;

    public ConformsAttribute(Type type)
        : base()
    {
        if (type.BaseType != typeof(Delegate) && type.BaseType != typeof(System.MulticastDelegate)) throw new Exception("Can only accept delegates");

        ConformTo = type.GetMethod("Invoke");
    }
}

将 EnforceConforms(typeof(myFavoriteClass)) 扔到一个类上,并将 Conforms(typeof(myFavoriteDelegate)) 扔到相关方法上,然后(这是 hacky 部分)typeof(myFavoriteClass).GetCustomAttributes(false)。您可以在静态初始化程序中执行此操作以“非常快”地失败,或者在测试类中执行此操作(如果您想花哨的话,它会在程序集中查找具有 EnforceConforms 属性的所有方法)。

通常,您可能不应该使用它。如果您的设计要求您检查正确的委托实现,则应尽可能重新架构。加上它的非编译时间位,所以你并没有真正节省很多时间。

于 2009-06-29T08:02:01.180 回答