With the public interface IGameTrack
, an implementation actually needs more data than what is publicly exposed by this interface. I came up with a pattern that just seems to work, i.e. explicit implementation, Array.Find
and casting, but I was wondering if there could be another approach for Game5.Install(IGameTrack)
:
public interface IGame
{
[NotNull]
[ItemNotNull]
IReadOnlyList<IGameTrack> Tracks { get; }
void Install([NotNull] IGameTrack track);
}
public interface IGameTrack
{
[NotNull]
string Name { get; } // nothing more needs to be exposed than this
}
internal sealed class GameTrack5 : IGameTrack // but custom implementation needs extra data
{
public GameTrack5([NotNull] string name, int position)
{
Name = string.IsNullOrWhiteSpace(name)
? throw new ArgumentException("Value cannot be null or whitespace.", nameof(name))
: name;
Position = position < 0
? throw new ArgumentOutOfRangeException(nameof(position))
: position;
}
public string Name { get; }
public int Position { get; }
string IGameTrack.Name => Name;
}
public sealed class Game5 : IGame
{
[NotNull]
[ItemNotNull]
private IReadOnlyList<GameTrack5> Tracks { get; } = new[]
{
new GameTrack5("abcd", 1234)
};
IReadOnlyList<IGameTrack> IGame.Tracks => Tracks;
public void Install(IGameTrack track) // works but is a bit weird
{
if (track == null)
throw new ArgumentNullException(nameof(track));
// TODO anything better than that ?
if (!(Array.Find(Tracks.Cast<IGameTrack>().ToArray(), s => s == track) is GameTrack5 result))
throw new ArgumentNullException(nameof(result));
var name = result.Name;
var position = result.Position; // our extra data we'll need
}
}
Question:
How to expose a public interface but its implementors requiring extra data ?
To make it clear, IGame.Install(IGameTrack)
will receive an IGameTrack
from which it should map to its specific implementation of IGameTrack
, in this case it will be GameTrack5
.
Edit:
Possible solution that does not expose implementations of IGameTrack
:
using System;
using System.Collections.Generic;
using OpenAG.Annotations;
// ReSharper disable once CheckNamespace
namespace Whatever
{
public interface IGame
{
string Name { get; }
IReadOnlyList<IGameTrack> Tracks { get; }
void Install([NotNull] IGameTrack track);
}
internal interface IGame<in T> : IGame where T : IGameTrack
{
void Install([NotNull] T track);
}
public interface IGameTrack
{
string Name { get; }
}
public class Game1 : IGame, IGame<GameTrack1>
{
public string Name { get; }
public IReadOnlyList<IGameTrack> Tracks { get; }
public void Install(IGameTrack track)
{
(this as IGame<GameTrack1>).Install(track as GameTrack1 ?? throw new InvalidOperationException());
}
void IGame<GameTrack1>.Install(GameTrack1 track)
{
if (track == null)
throw new ArgumentNullException(nameof(track));
throw new NotImplementedException();
}
}
internal class GameTrack1 : IGameTrack
{
public string Name { get; }
}
}
Aucun commentaire:
Enregistrer un commentaire