简体   繁体   中英

Declaring a Generic type must implement the Generic Constraint

I would ideally like Foo<T> to require that any instantiations themselves implement the T . ( Foo<> is either abstract, or an interface)

Or if it's actually MyFoo : IFoo<ConcreteClass> then MyFoo : ConcreteClass

Currently I've achieved my underlying goal by having Foo<T> declare a property of type T, which is generally satisfied by returning the class itself, but I'm curious as to whether I can do it more directly.


EDIT: I've convinced myself this is impossible. For reference the code in question looks like this:

 public abstract class BotController<TBot> : BotController, IBotController<TBot> { protected BotController(TBot wrappedBot, PlayerRecord player, long timeout) : base(player, timeout) { WrappedBot = wrappedBot; } protected TBot WrappedBot { get; set; } public abstract TBot ControlledBot { get; } private string cachedName; public override string BotName => (cachedName = (cachedName ?? WrappedBot.Name)); protected T SafelyPerformBotActionWithTimer<T>(Func<TBot, T> botAction, string errorDescriptor) { return SafelyPerformBotActionWithTimer(() => botAction(WrappedBot), errorDescriptor); } protected void SafelyPerformBotActionWithTimer(Action<TBot> botAction, string errorDescriptor) { SafelyPerformBotActionWithTimer(() => botAction(WrappedBot), errorDescriptor); } } public class BattleshipsController : BotController<IBattleshipsBot>, IBattleshipsBot { public BattleshipsController(IBattleshipsBot battleshipImplementation, PlayerRecord playerRecord, long timeout = 1000) : base(battleshipImplementation, playerRecord, timeout) {} public override IBattleshipsBot ControlledBot => this; public IEnumerable<IShipPosition> GetShipPositions() { //qqMDM validate Ship Positions here? return SafelyPerformBotActionWithTimer(b => b.GetShipPositions(), "specifying ship positions"); } public IGridSquare SelectTarget() { var target = SafelyPerformBotActionWithTimer(b => b.SelectTarget(), "selecting a target"); if (target.IsOutsideGrid()) { throw new ShotOffBoardException($"{BotName} has tried to shoot off the board, aiming at square {target.Row}{target.Column}", this); } return target; } public void HandleShotResult(IGridSquare square, bool wasHit) { SafelyPerformBotActionWithTimer(b => b.HandleShotResult(square, wasHit), "handling shot result"); } public void HandleOpponentsShot(IGridSquare square) { SafelyPerformBotActionWithTimer(b => b.HandleOpponentsShot(square), "handling opponent's shot"); } public string Name => BotName; }

The specific thing I was imagining is not possible.

I wanted some syntax along the lines of:

abstract class Foo<TWrappedType> where Foo : TWrappedType

which might conceptually work if TWrappedType is an interface (which it will be in my cases), but could never work if TWrappedType is a concrete class, because C# doesn't do multiple inheritance.

Since you can't constrain whether a Generic Type is an interface or a class, the syntax cannot exist.

I'd sort of gotten half way towards that as I was writing the question, hence the confused question, but not far enough to work out that it was impossible.

If I've understood you correctly, you could do this with an intermediate class/interface:

interface IInterface<T>
{ }

class Foo<T> : IInterface<T> where T : IInterface<T>
{
}

But your question isn't all that clear. I'm not sure if this is anything similar to what you want, these kind of class declarations simply make my head hurt.

For the benefit of future people considering this, it initially seemed as though you could get some benefit and end up with the same syntax at the endpoint (a Foo<T> that looks like a T ), by setting up explicit and implicit cast operators that simply return the property of the abstract class.

However this is not allowed in the language.

See here: implicit operator using interfaces

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM