C# 调用具有不同类型的相同扩展函数作为参数(可能是委托?)
Posted
技术标签:
【中文标题】C# 调用具有不同类型的相同扩展函数作为参数(可能是委托?)【英文标题】:C# call same extended function with different type as parameter (maybe delegates ?) 【发布时间】:2021-11-13 00:17:24 【问题描述】:我正在尝试为我的项目构建一个规则检查器,该检查器在规则列表中循环,如果其中任何一个的标志为真,则返回我。 下面是我作为示例编写的代码(不是实际代码,请忽略最终未观察到的最佳实践或细节)。
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”使用相同的规则检查器,并且可以重用这些规则或添加新的规则,如具有自己的触发逻辑的“模块”。
上面的代码不起作用,因为当我调用通用 void(UpdateMe<ICounterValued>
和 UpdateMe<ITimeValued>
)时,数据值无法访问我在接口内定义的属性(CounterValue
,CounterLimit
,ElapsedTime
, TimeLimit
)。
我假设这种 void 泛型方法存在一些转换或接口问题;我没有必要使用泛型,但我没有找到更聪明的解决方案;我已经在Rule
类中尝试了将代表作为UpdateMe
的参数的一些东西,但是没有任何东西可以用不同的类型参数进行扩展,并让规则检查器通过为每个参数传递MyMainClass
的实例来调用所有内容。
感谢 andavace 的任何建议。
【问题讨论】:
你得到的确切错误是什么,错误到底在哪里? 我想,但我不确定问题是你在Rule.UpdateMe<T>
中对T
没有限制,允许访问属性。我想我可以创建一个基本接口public interface IValued int Value get; int Limit get;
,然后显式实现ICounterValued
和ITimeValued
(暂时忽略int/float)。到目前为止我找不到魔法酱。我希望这个漫无边际的帮助
@Flydog57 错误为'ITimeValued' does not contain a definition for 'ElapsedTime' and no accessible extension method 'ElapsedTime' accepting a first argument of type 'ITimeValued' could be found (are you missing a using directive or an assembly reference?)
,所有其他属性都相同。如果我总是知道界面内将包含什么类型的数据,则可以完成您的解决方案。我正在尝试实现的实际用途,例如,使用更复杂的接口,例如:``` public interface ITimer AdvancedTimer Timer get; ```
是的,我复制了你的代码并试图让它编译,得到了同样的错误。这是因为T
缺少约束。你不能在T
上调用某些东西,除非T
以允许调用发生的方式受到限制。我现在想已经太晚了,但你的问题让我很感兴趣。一旦我离开,我可能会想出一个解决方案。
@Flydog57 我认为你是对的,UpdateMe
的覆盖使 data
属性被视为通用 T
值,而不是被覆盖的接口类型。泛型解决方案看起来太有限,无法做如此不均匀的事情;也许与返回 bool
的代表的事情是可行的。
【参考方案1】:
以这种方式构建(您的方法)对我来说更有意义:
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);
按照你的方法,我无法弄清楚为什么 X 类会有一个可以将 Y 类传递给的规则列表;它们适用的规则和类在我的脑海中是联系在一起的。因此我在构建时建立了这个链接
..但即使那样我也不知道我是否会这样做
我想我更有可能做这样的事情:
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);
【讨论】:
第一个代码是最接近的,我只是在我的代码中尝试了解决方案,通过一些调整,我可以让它在规则的构造函数中发送实例并将 MyMainClass 实例存储为像你一样的接口写道:private ICounterValued _thing;
。扩展 RuledClass 不是一个好主意,因为 MyMainClass 通常需要扩展另一个不是 Ruled 的基类。第二种解决方案过于面向我的示例代码,例如,我必须在每个需要 TimeLimit 规则或 Counter Rule 的类中编写“违规逻辑”。
我只做了一个 RuledClass 因为我不想每次都提供一个 rules 属性定义;如果您乐于在 IRuleable 的任何地方重复规则定义,则可以将其设为接口。 第二种解决方案过于以我的示例代码为导向 - 这就是我害怕提供人为示例的结果.. 我必须编写“违规逻辑” - 但是通过使其成为接口,您必须“在每个需要 X 的类中编写更多属性”。第二个的逻辑非常简单,并且确实允许一个类设置自己疯狂的变化规则。
..没有无休止的接口,它们的实现以及支持它们的实例。.正如所呈现的那样,它使一个类负责检查它是否有效,但我认为没有理由外部类无法给出位于数组中的规则集Func
s【参考方案2】:
我找到了一个似乎可行的解决方案,并且与我正在寻找的解决方案接近。 @Caius Jard 的第一个解决方案也有效,但我认为最好避免将检查实例指向规则的变量中。 在我的问题中的相同示例代码下方,但更新代码旁边有 cmets:
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
派生类的方法,将检查实例作为通用object
传递。任何扩展 Rule
的类都可以实现他的规则逻辑覆盖 IsTriggered
,并在内部进行显式转换,然后最终开始检查。
【讨论】:
以上是关于C# 调用具有不同类型的相同扩展函数作为参数(可能是委托?)的主要内容,如果未能解决你的问题,请参考以下文章