3 ms·
Well, that's flattering I guess :) But it's not as bad as it sounds and the only way (AFAIK) to be able to work with both polymorphic objects and have the conv
by mhomde 9y ago
Well, that's flattering I guess :)
But it's not as bad as it sounds and the only way (AFAIK) to be able to work with both polymorphic objects and have the convenience (and type checking) of a generic counterpart. Limiting yourself to just a generic object/interface unfortunately breaks a lot of design patterns. It's also safe afaik since the only thing the generic version does is some casting and it's generic parameter enforces that casting.
but if you have a better way I'm genuinely interested to hear it
- louthy 9y agoI agree with @ahoka that the advice is bad. > Limiting yourself to just a generic object/interface unfortunately breaks a lot of design patterns. Which ones and why? > It's also safe afaik since the only thing the generic version does is some casting and it's generic parameter enforces that casting. If you don't have a known constraint that is enforced by the type system then it can never be 'safe'.
- mhomde 9y agoYou can't pass generic interfaces to other classes that should handle non-generic interfaces (that aren't dependent on the generic constraint) >If you don't have a known constraint that is enforced by the type system then it can never be 'safe'. I don't quite know what you mean by this. If you cast to your "T" then casting is ensured. I guess there's some margin for error in that you must enter your casting code to be correct, but it's pretty hard to mess that part up, granted not as good as enforced genericy. Note that in many instances you only initialize generic classes, but you needed to be extract a non-generic interface from then to pass to other classes.
- louthy 9y ago> You can't pass generic interfaces to other classes that should handle non-generic interfaces (that aren't dependent on the generic constraint) I'd like to see an example of what you mean. You can absolutely pass generic values to other classes. If the type you're passing the generic value to is not generic then the method should be. If you are casting then 99 times out of 100 you have a problem with your code because the types aren't compatible. i.e. static void DoSomethingGeneric<A>(A value) => ... static void DoSomethingSpecific(string value) => ... IEnumerable<int> array = new [] { 1, 2, 3, 4, 5 }; DoSomethingGeneric(array.First()); // Works DoSomethingSpecific(array.First()); // Can only ever work if array is a string[] > If you cast to your "T" then casting is ensured. If you 'get out' of the generic situation by casting to an object or dynamic, then at some point you're going to have to cast back to a concrete type. That is the point where your code will blow up. You're carrying ticking time bombs as values.
- mhomde 9y agoHi, sorry for the delay, had to pop into a meeting. I tried to make a very scaled down example of what I mean. It's hard to imagine a real-world scenario from this but assume that according to the architecture we need to separate concerns this way. The point is that in some cases you want to be able to pass a more general interface while getting some Type-checking and convenience of generics. In this case we want to hold on to PopProtocolSender and perhaps use it as a variable somewhere. When we use it we need to acccess it's specific IPopProtocol Protocol Property. But we also want to be able to treat it as a general IProtocolSender that has a IProtocol Property, so it can be sent to other classes that takes that interface. These kinds of situations mainly arise when you have a more complicated architecture. If there's a better way to have your cake and eat it too I'd be glad to know it :) public interface IPopProtocol : IProtocol { void SomeUniqueMetod(); } public interface IProtocol { } public interface IProtocolManager { void SendMessage(IProtocol protocol, string message); } public interface IProtocolSender { IProtocol Protocol { get; } } public class PopProtocol : IPopProtocol { public void SomeUniqueMetod() { } } public class Program { public void DoStuff() { var sender = new PopProtocolSender (); var manager = new ProtocolManager(); manager.SendMessage(sender.Protocol, "Testing"); sender.Protocol.SomeUniqueMethod(); } } public class ProtocolManager : IProtocolManager { public void SendMessage(IProtocol protocol, string message) { } } public class ProtocolSender : IProtocolSender { protected readonly IProtocol _protocol; public ProtocolSender(IProtocol protocol) { _protocol = protocol; } public IProtocol Protool => _protocol; } public class PopProtocolSender : ProtocolSender<IPopProtocol> { public PopProtocolSender() : base(new PopProtocol) { } } public class ProtocolSender<T> : ProtocolSender where T : IProtocol { public ProtocolSender(T protocol) : base(protocol) { } public new T Protocol => (T)_protocol; }
- GenericsMotors 9y agoI understand what you're getting at, but why not simply make both IProtocolSender and ProtocolSender generic. Here's a functioning example I based on yours that just compiled and ran in LinqPad without any issues: public interface IPopProtocol : IProtocol { void SomeUniqueMethod(); } public interface IProtocol { } public interface IProtocolManager { void SendMessage(IProtocol protocol, string message); } public interface IProtocolSender<out T> where T : IProtocol { T Protocol { get; } } public class PopProtocol : IPopProtocol { public void SomeUniqueMethod() { Console.WriteLine($"Hi, I'm {nameof(PopProtocol)}"); } } void Main() { var sender = new ProtocolSender<IPopProtocol>(new PopProtocol()); var manager = new ProtocolManager(); manager.SendMessage(sender.Protocol, "Testing"); sender.Protocol.SomeUniqueMethod(); } public class ProtocolManager : IProtocolManager { public void SendMessage(IProtocol protocol, string message) { } } public class ProtocolSender<T> : IProtocolSender<T> where T : IProtocol { private T _protocol; public T Protocol => _protocol; public ProtocolSender(T protocol) { _protocol = protocol; } } Not sure what the fuss is: you can use SendMessage as usual, and also have access to SomeUniqueMethod. EDIT: Also please note that the signature for the generic IProtocolSender uses the out keyword, making it covariant. This will allow you to cast IProtocolSender<IPopProtocol> to IProtocolSender<IProtocol> if you need to, or to pass an IProtocolSender<IPopProtocol> object to a method that expects an IProtocolSender<IProtocol>.
- Quarrelsome 9y ago> have a generic version that overrides the properties (with new) is the horrible bit. You should almost NEVER override with "new".
- mhomde 9y agoI agree, this is one of the few exceptions, in fact the only, exception I encountered. But this is a solution for a very specific problem in certain solutions, not a something you tend to do all the time