C# 调用具有不同类型的相同扩展函​​数作为参数(可能是委托?)

C# call same extended function with different type as parameter (maybe delegates ?)

我正在尝试为我的项目构建一个规则检查器,该检查器循环通过规则列表,如果其中任何一个具有 true 标志,则 return 我。 在我编写的代码下方,作为我正在尝试做的示例(不是实际代码,请忽略最终未观察到的最佳实践或细节)。

using System.Collections;
using System.Collections.Generic;
using System.Linq;

public interface ICounterValued {
    int CounterValue {get;}
    int CounterLimit {get;}
}

public interface ITimeValued {
    float ElapsedTime {get;}
    float TimeLimit {get;}
}

public class MyMainClass : ICounterValued , ITimeValued {
    // Counter data
    private int     _counterValue   = 0;
    private int     _counterLimit   = 10;
    // Time data
    private float   _elapsedTime    = 0;
    private float   _timeLimit      = 10f;
    // ICounterValued
    public int      CounterValue    => _counterValue;
    public int      CounterLimit    => _counterLimit;
    // ITimeValued
    public float    ElapsedTime     => _elapsedTime;
    public float    TimeLimit       => _timeLimit;

    public RuleChecker  ruleChecker;

    public void InitializeMe(){
        ruleChecker = new RuleChecker();
        ruleChecker.rules.Add( new TimeRule() );
        ruleChecker.rules.Add( new CounterRule() );
    }

    public void UpdateRuleChecker(){
        ruleChecker.CheckRules<MyMainClass>( this );
    }

    public bool NeedToStop(){
        return ruleChecker.SomeRuleTriggered();
    }
}

public class RuleChecker {
    public List<Rule> rules = new List<Rule>();

    public void CheckRules<T>( T data ){
        rules.ForEach( x => x.UpdateMe<T>(data) );
    }

    public bool SomeRuleTriggered(){
        return rules.Any( x => x.IsTriggered() );
    }
}

public abstract class Rule {
    protected bool _triggered;

    public virtual bool IsTriggered() =>  _triggered;
    // check logic that will set the _triggered flag to true
    public abstract void UpdateMe<T>( T data );
}

public class TimeRule : Rule {
    public override void UpdateMe<ITimeValued>( ITimeValued data ){
        _triggered = (data.ElapsedTime >= data.TimeLimit);
    }
}

public class CounterRule : Rule {
    public override void UpdateMe<ICounterValued>( ICounterValued data ){
        _triggered = (data.CounterValue >= data.CounterLimit);
    }
}

重点是将来对具有一个或多个接口的不同“MainClasses”使用相同的规则检查器,并有可能重用该规则或添加新的规则,如“模块”和他自己的触发逻辑.

上面的代码不起作用,因为当我调用通用 voids(UpdateMe<ICounterValued>UpdateMe<ITimeValued>)时,数据值无法访问我在接口中定义的属性(CounterValueCounterLimit,ElapsedTime,TimeLimit).

我假设这种 void 泛型方法存在一些转换或接口问题;我没有必要使用泛型,但我没有找到更聪明的解决方案;我已经尝试过使用委托作为 Rule class 中 UpdateMe 的参数,但没有任何东西可以用不同类型的参数扩展,并让规则检查器通过传递实例来调用所有每个 MyMainClass

感谢andavace的任何建议。

以这种方式构建(您的方法)对我来说更有意义:

public class RuledClass{
    public List<Rule> Rules = new List<Rule>();
    
    public bool AnyViolated(){
        return Rules.Any(r => r.IsViolated());
    }
}

public class MyMainClass : RuledClass, ICounterValued , ITimeValued {
    // Counter data
    public int     CounterValue {get;set;} 
    public int    CounterLimit  {get;set;} 
    // Time data
    public float  ElapsedTime   {get;set;} 
    public float  TimeLimit   {get;set;}   
    // ICounterValued
    

    public MyMainClass(){
        Rules.Add( new TimeRule(this) );
        Rules.Add( new CounterRule(this) );
    }
}

public abstract class Rule {
    public abstract bool IsViolated();
}

public class TimeRule : Rule {
    private ITimeValued _thing;
    public TimeRule(ITimeValued thing){
        _thing = thing;
    }
    public override bool IsViolated(){
        return (_thing.ElapsedTime >= _thing.TimeLimit);
    }
}
public class CounterRule : Rule {
    private ICounterValued _thing;
    public CounterRule(ICounterValued thing){
        _thing = thing;
    }
    public override bool IsViolated(){
        return (_thing.CounterValue >= _thing.CounterLimit);
    }
}

我想不通,用你的方法,为什么 class X 会有一个规则列表,可以将 class Y 传递给这些规则;规则和它们适用的 class 在我的脑海中是联系在一起的.. 因此我在构建时 link

..但即使那样我也不知道我是否会这样做

我认为我更有可能这样做:

public class RuledClass{
    public List<Func<bool>> Rules = new List<Func<bool>>();
    
    public bool AnyViolated(){
        return Rules.Any(r => r());
    }
}

public class MyMainClass : RuledClass {
    // Counter data
    public int     CounterValue {get;set;} 
    public int    CounterLimit  {get;set;} 
    // Time data
    public float  ElapsedTime   {get;set;} 
    public float  TimeLimit   {get;set;}   
    // ICounterValued
    

    public MyMainClass(){
        Rules.Add(()=>ElapsedTime>TimeLimit);
        Rules.Add(()=>CounterValue>CounterLimit);
    }
}

我找到了一个似乎有效并且接近我所寻找的解决方案。 @Caius Jard 的第一个解决方案也有效,但我认为最好避免将检查实例一直指向规则变量内。 在我的问题中的相同示例代码下方,但更新代码旁边有注释:

using System.Collections;
using System.Collections.Generic;
using System.Linq;

public interface ICounterValued {
    int CounterValue {get;}
    int CounterLimit {get;}
}

public interface ITimeValued {
    float ElapsedTime {get;}
    float TimeLimit {get;}
}

public class MyMainClass : ICounterValued , ITimeValued {
    // Counter data
    private int     _counterValue   = 0;
    private int     _counterLimit   = 10;
    // Time data
    private float   _elapsedTime    = 0;
    private float   _timeLimit      = 10f;
    // ICounterValued
    public int      CounterValue    => _counterValue;
    public int      CounterLimit    => _counterLimit;
    // ITimeValued
    public float    ElapsedTime     => _elapsedTime;
    public float    TimeLimit       => _timeLimit;

    public RuleChecker  ruleChecker;

    public void InitializeMe(){
        ruleChecker = new RuleChecker();
        ruleChecker.rules.Add( new TimeRule() );
        ruleChecker.rules.Add( new CounterRule() );
    }

    // I don't need this anymore, the condition will be checked
    // inside SomeRuleTriggered
    // public void UpdateRuleChecker(){
    //     ruleChecker.CheckRules<MyMainClass>( this );
    // }

    public bool NeedToStop(){
        // The instance will be passed as argument
        // return ruleChecker.SomeRuleTriggered();
        return ruleChecker.SomeRuleTriggered( this );
    }
}

public class RuleChecker {
    public List<Rule> rules = new List<Rule>();

    // As commented above, I don't need to update a stored 
    // flag in Rule (and direved) anymore
    // public void CheckRules<T>( T data ){
    //     rules.ForEach( x => x.UpdateMe<T>(data) );
    // }

    // Now the instance will be passed as a generic object and parsed
    // inside IsTriggered method
    // public bool SomeRuleTriggered(){
    //     return rules.Any( x => x.IsTriggered() );
    // }
    public bool SomeRuleTriggered( object target ){
        return rules.Any( x => x.IsTriggered( target ) );
    }
}

public abstract class Rule {
    // No need to update a falg anymore
    // protected bool _triggered;

    // No need to update a falg anymore
    // public virtual bool IsTriggered() =>  _triggered;

    // No need to update a falg anymore
    // public abstract void UpdateMe<T>( T data );
    public abstract bool IsTriggered( object target );
}

public class TimeRule : Rule {
    // No need to update a falg anymore
    // public override void UpdateMe<ITimeValued>( ITimeValued data ){
    //     _triggered = (data.ElapsedTime >= data.TimeLimit);
    // }
    public override bool IsTriggered( object target ){
        ITimeValued target_timed = target as ITimeValued;
        if( null == target_timed)
            return false;

        return (target_timed.ElapsedTime >= target_timed.TimeLimit);
    }
}

public class CounterRule : Rule {
    // No need to update a falg anymore
    // public override void UpdateMe<ICounterValued>( ICounterValued data ){
    //     _triggered = (data.CounterValue >= data.CounterLimit);
    // }
    public override bool IsTriggered( object target ){
        ICounterValued target_counter = target as ICounterValued;
        if( null == target_counter)
            return false;

        return (target_counter.CounterValue >= target_counter.CounterLimit);
    }
}

如您所见,RuleChecker 可以在任何 Rule 派生的 class 上调用该方法,将检查实例作为通用 object 传递。任何扩展 Rule 的 class 都可以实现他的规则逻辑覆盖 IsTriggered 并在内部进行显式转换,然后最终开始检查。