C# 允许客户端委托重写默认行为的模式

C# 允许客户端委托重写默认行为的模式,c#,delegates,C#,Delegates,我有一个C#类,它有一些功能,我想允许该类的客户机重写这些功能。具体而言,我希望: 如果客户机什么也不做,他们就会得到我们的默认行为 客户端可以重写该行为并导致不调用我们的默认行为 客户机可以重写该行为,并将我们的默认行为作为前/后/中间操作调用 我目前的想法是: 公开一个事件,如果客户端希望覆盖该行为,则客户端将订阅该事件 公开具有默认行为(与事件签名匹配)的方法: void DefaultBehavior() 每当我们试图触发事件时,如果委托为null,我们将调用默认的行为方法,否则,我们将

我有一个C#类,它有一些功能,我想允许该类的客户机重写这些功能。具体而言,我希望:

  • 如果客户机什么也不做,他们就会得到我们的默认行为
  • 客户端可以重写该行为并导致不调用我们的默认行为
  • 客户机可以重写该行为,并将我们的默认行为作为前/后/中间操作调用
  • 我目前的想法是:

  • 公开一个事件,如果客户端希望覆盖该行为,则客户端将订阅该事件
  • 公开具有默认行为(与事件签名匹配)的方法:
    void DefaultBehavior()
  • 每当我们试图触发事件时,如果委托为null,我们将调用默认的行为方法,否则,我们将调用事件的委托:

    if (eventDelegate == null)
        DefaultBehavior();
    else
        eventDelegate();
    
  • 当客户端重写该行为时,他们可以选择调用我们的DefaultBehavior方法来获得所需的行为


    你认为从客户的角度来看,这种方法足够直观吗?或者你能提出更好的替代方案吗?

    好吧,如果你想让你的客户端覆盖某些行为,为什么不创建一个虚拟函数,让客户端实际覆盖它呢


    这是实现这一目标的直截了当的方法。无需重新发明轮子。

    如果我理解你的问题,那么通过
    客户端
    你指的是一些调用你的类的代码,而不是重写你的类,假设你需要这样做(如果你可以这样做,我同意@zmbq-answer):

    您可以使您的方法有2个重载,一个没有参数,另一个接收
    操作
    对象

    public void methodName()
    public void methodName(Action delegate)
    
    然后在
    methodName()
    的主体中,您将使用defaultAction调用另一个方法

    public void methodName()
    {
      methodName(DefaultBehavior);
    }
    
    最后,在第二个方法中,您只需调用作为参数传递的委托,而不管它是否为默认值

    public void methodName(Action delegate)
    {
     delegate();
    }
    
    类的客户端将看到这两个重载,并决定是使用默认行为还是提供自定义行为

    编辑: 好的,最后一次尝试:),根据您最后的评论,有一个实例字段对您有效吗

    private Action behaviorDelegate = DefaultBehavior;
    

    在类中的任何位置,您都可以为
    behaviorDelegate
    指定不同的行为,这样您就不需要
    if
    语句,因为无论该行为是否为默认行为,该行为始终位于
    delegate
    变量中。这不是一个很大的变化,但对我来说似乎更干净。

    只需使用虚拟方法:

    public class BaseClass {
       public virtual void Something() {
          Console.WriteLine("base functionality");
       }
    }
    
    public class Sub1 : BaseClass {
       public override void Something() {
           // do whatever you want here
           base.Something();  // don't call it at all if you like
           // do whatever you want here
       }
    }
    
    您也可以(或许?)在不发生事件的情况下完成:

    声明:

    public class DemoClass
    {
        public delegate string MyDelegate(string testValue);
    
        public static MyDelegate DefaultBehavior
        {
            get
            {
                return testValue => 
                  { 
                     return String.Concat(testValue, 
                                          ", now with 99% more exclamation points!!!!!!!!"); 
                  };
            }
        }
    
        public MyDelegate ModifyString = testValue => 
            { 
               return DemoClass.DefaultBehavior(testValue); 
            };
    }
    
    // first, default:
    DemoClass demoObject = new DemoClass();
    Console.WriteLine(demoObject.ModifyString("My test string")); 
    
    // now, pure "override":
    demoObject.ModifyString = testVal => 
            { return String.Concat(testVal, ", this time with question marks????"); 
            };
    Console.WriteLine(demoObject.ModifyString("Second test string"));
    
    // finally, define a delegate that overrides and calls default:
    DemoClass.MyDelegate combined = testVal => 
            { return String.Concat(DemoClass.DefaultBehavior(testVal), 
                                   ", now we're really tricky"); 
            };
    demoObject.ModifyString = combined;
    Console.WriteLine(demoObject.ModifyString("Third test string"));
    
    My test string, now with 99% more exclamation points!!!!!!!! Second test string, this time with question marks???? Third test string, now with 99% more exclamation points!!!!!!!!, now we're really tricky 使用:

    public class DemoClass
    {
        public delegate string MyDelegate(string testValue);
    
        public static MyDelegate DefaultBehavior
        {
            get
            {
                return testValue => 
                  { 
                     return String.Concat(testValue, 
                                          ", now with 99% more exclamation points!!!!!!!!"); 
                  };
            }
        }
    
        public MyDelegate ModifyString = testValue => 
            { 
               return DemoClass.DefaultBehavior(testValue); 
            };
    }
    
    // first, default:
    DemoClass demoObject = new DemoClass();
    Console.WriteLine(demoObject.ModifyString("My test string")); 
    
    // now, pure "override":
    demoObject.ModifyString = testVal => 
            { return String.Concat(testVal, ", this time with question marks????"); 
            };
    Console.WriteLine(demoObject.ModifyString("Second test string"));
    
    // finally, define a delegate that overrides and calls default:
    DemoClass.MyDelegate combined = testVal => 
            { return String.Concat(DemoClass.DefaultBehavior(testVal), 
                                   ", now we're really tricky"); 
            };
    demoObject.ModifyString = combined;
    Console.WriteLine(demoObject.ModifyString("Third test string"));
    
    My test string, now with 99% more exclamation points!!!!!!!! Second test string, this time with question marks???? Third test string, now with 99% more exclamation points!!!!!!!!, now we're really tricky 输出:

    public class DemoClass
    {
        public delegate string MyDelegate(string testValue);
    
        public static MyDelegate DefaultBehavior
        {
            get
            {
                return testValue => 
                  { 
                     return String.Concat(testValue, 
                                          ", now with 99% more exclamation points!!!!!!!!"); 
                  };
            }
        }
    
        public MyDelegate ModifyString = testValue => 
            { 
               return DemoClass.DefaultBehavior(testValue); 
            };
    }
    
    // first, default:
    DemoClass demoObject = new DemoClass();
    Console.WriteLine(demoObject.ModifyString("My test string")); 
    
    // now, pure "override":
    demoObject.ModifyString = testVal => 
            { return String.Concat(testVal, ", this time with question marks????"); 
            };
    Console.WriteLine(demoObject.ModifyString("Second test string"));
    
    // finally, define a delegate that overrides and calls default:
    DemoClass.MyDelegate combined = testVal => 
            { return String.Concat(DemoClass.DefaultBehavior(testVal), 
                                   ", now we're really tricky"); 
            };
    demoObject.ModifyString = combined;
    Console.WriteLine(demoObject.ModifyString("Third test string"));
    
    My test string, now with 99% more exclamation points!!!!!!!! Second test string, this time with question marks???? Third test string, now with 99% more exclamation points!!!!!!!!, now we're really tricky 我的测试字符串,现在有99%以上的感叹号!!!!!!!! 第二个测试字符串,这次带问号???? 第三个测试字符串,现在有99%以上的感叹号!!!!!!!!,现在我们真的很狡猾
    客户机正在使用my类,而该类不是从中派生的。我应该说得更清楚。谢谢。实际上,你应该改变这一点,让它可以派生。整个类都可以派生是没有意义的(我知道,我没有提供足够的信息)。但您让我想到另一种方法是公开一个属性(使用setter),该属性返回一个可派生的类(使用虚拟方法)。如果客户机没有设置这个属性,那么他们将得到我们的默认行为。如果他们确实设置了它,他们可以传入派生类并重写该行为。谢谢。我想我的问题不清楚(对不起)。该方法将在适当的时间由类在内部调用。--它不是由类的客户端调用的(这就是我使用事件的原因)。我需要允许他们为我提供适当的功能,我将在以后在类内部调用这些功能,然后你仍然可以使用这种方法,如果我需要澄清一些我喜欢的东西,请告诉我,但这种方法意味着客户端必须显式调用该方法,从这个问题来看,似乎是原始类决定了何时调用它。客户端应该只决定是接受默认行为还是覆盖默认行为。@FrancescoBaruchelli。正确:这就是为什么这种方法在我的情况下不起作用的原因。这种行为是我想要的,除了类不是从中派生出来的。@FrancescoBaruchelli,谢谢你的反馈。谢谢。除了没有“event”关键字之外,这种方法与我最初的方法几乎相同——这意味着客户端可以完全覆盖ModifyString委托(而不仅仅是订阅/取消订阅)。在IMO中,这不会更好,因为如果覆盖ModifyString,则可能会覆盖以前的委托。通过使用event关键字,您可以防止出现这种情况(因为它只允许订阅/取消订阅,而不允许覆盖底层委托)。