I've got something like this:
// This gets implemented by plugin authors to get callbacks about various things.
public interface ExternalPlugin
{
// This gets called by the main application to tell the plugin some data
// is available or similar.
void DoStuff(SomeDataBlob blob);
}
// Data blob for v1 of API
public class SomeDataBlob
{
internal SomeDataBlob(string prop) { Prop = prop; }
// Some piece of data that V1 plugins need
public string Prop { get; private set; }
}
// FUTURE!
// Data blob API v2 of API
public class SomeDataBlobV2 : SomeDataBlob
{
// Can be passed to clients expecting SomeDataBlob no problem.
internal SomeDataBlobV2(string prop, string prop2) :base(prop) { Prop2 = prop2; }
// Some piece of data that V2 plugins need. V2 plugins can cast to this from
// SomeDataBlob, but still can load successfully into older versions that support
// only V1 of the API
public string Prop2 { get; private set; }
}
I have to make SomeDataBlob
public so that it can be used as a member of the public interface method ExternalPlugin.DoStuff
. However, I would not like to allow clients to inherit from that class and thus be susceptible to the brittle base class problem. (All derivatives of that class should be kept in the same assembly)
Marking the class sealed
goes too far because I believe removing sealed
is a breaking API change; and even if that isn't, once I ship SomeDataBlobV2
clients could still do the wrong thing and inherit from SomeDataBlob
directly.
Is there a way to enforce this kind of pattern?
Make the class internal, and expose an interface instead. Then use the factory pattern to create the correct implementation.
public interface ISomeDataBlob
{
}
internal class SomeDataBlob : ISomeDataBlob
{
}
public class BlobApiFactory
{
ISomeDataBlob Create();
}
You hide the implementation, but still give the user access to everything. You even make unit tests easier for your users ;)
Edit (answer to a comment from the OP)
What I effectively want is some method taking some parameters. I want to be able to add parameters that the main application can provide in a future version if the API without breaking clients. But I don't want clients to be able to create instances of the "parameters" class/interface or otherwise interact with it beyond receiving an instance of it as a parameter
Instead of hiding the APIs you can make sure that all object passed to your library originates from your assembly:
public class YourCoolService
{
public void DoSomething(ISomeDataBlob blob)
{
if (blob.GetType().Assembly != Assembly.GetExecutingAssembly())
throw new InvalidOperationException("We only support our own types");
}
}
Edit2
Just noticed that @RQDQ already provided that solution (didn't notice when answering your comment). If that's the solution you want, accept his answer instead.
/// <summary>
/// This is a dummy constructor - it is just here to prevent classes in other assemblies
/// from being derived from this class.
/// See http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2971840&SiteID=1
/// </summary>
internal MhlAdminLayer() { }
The idea is to have a constructor with no parameters internal. Then the class can't be derived from.
Edit: Sorry, the link in the comment doesn't work any more.
Edit 2: http://msdn.microsoft.com/en-us/library/vstudio/ms173115.aspx
"You can prevent a class from being instantiated by making the constructor private ... "
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With