0

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;
}
Brondahl
  • 7,402
  • 5
  • 45
  • 74
  • 1
    How you are now doing it is the way to go, as far as I know. – Evk Feb 21 '18 at 11:57
  • 1
    I might have misunderstood you, but *why* would you like to do this? And are you intentionally mixing `IFoo` with `Foo`? – flindeberg Feb 21 '18 at 12:06
  • 1
    I'm sorry, but I've read the question 3 times and still don't understand what you want. Is it to force `T` to be a concrete implementation (so `Foo>` will fail, while `Foo>` will succeed)? – Zohar Peled Feb 21 '18 at 12:13
  • I've concluded that the specific thing I was imagining is not possible, and the oddities about the interface or the concrete in the question are actually the symptoms of why it's impossible. Answer to follow. – Brondahl Feb 21 '18 at 12:16

3 Answers3

0

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.

Brondahl
  • 7,402
  • 5
  • 45
  • 74
  • Where is the multiple inheritance here? – InBetween Feb 21 '18 at 19:15
  • `Foo` is abstract, so there must be a concrete class deriving from it, `MyFoo`, say. Then `MyFoo : Foo`. But I want `MyFoo` to extend `T`. So `MyFoo : Foo,T` but if `T` is Concrete then you have multiple inheritance (or a very boring class). – Brondahl Feb 22 '18 at 00:38
  • Abstract `Foo` extends `T` and `MyFoo` extends `Foo` which implicitly makes it extend `T`. Sorry, I still fail to see any multiple inheritance here, its just a simple inheritance chain of three types: `T -> Foo -> MyFoo` – InBetween Feb 22 '18 at 07:09
  • Sure ... but now I can't use an arbitrary `T` which renders the whole thing pointless. I want to define `MyFoo` Or I have to define a separate `Foo` for each base `T`, again rendering the effort worthless. – Brondahl Feb 22 '18 at 10:25
0

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.

InBetween
  • 32,319
  • 3
  • 50
  • 90
-1

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

Brondahl
  • 7,402
  • 5
  • 45
  • 74
  • Except implicit cast will not work if T is interface (like in your case). – Evk Feb 21 '18 at 13:25
  • @-Andrei Gheorghiu, @-Sunil, @-AsifAli72090, @-MickyD, @Evk, I've editted this to note the conceptual thought, and the fact that it's not possible, including a citation. Don't know whether you think that makes it a valuable answer now? Happy for it to be re-deleted if not. – Brondahl Feb 22 '18 at 10:30
  • Honestly, I don't know why you just didn't edit your existing answer. Your answers read as a daily diary –  Feb 22 '18 at 13:13
  • @MickyD Because "It is impossible to do this thing you have asked for" is a different answer from "You could improve this other thing that you mentioned", and also different from "In case you were thinking of it .. you can't actually do this other thing that might have been an improvement." If you don't think it's appropriate here, I'm happy for you to VTC. – Brondahl Feb 22 '18 at 17:09