0

I'm writing a class library with a class called Application. The Application class contains a collection of Document objects. I want the Application class to be solely responsible for "creating" (constructing) Document objects when appropriate. I do not want myself or other our developers to be able to construct new Document objects arbitrarily.

I've tried to come up with some robust way to enforce this, but I've only come up with a couple possibilities, and they both have drawbacks.

The first way would be to nest the Application class within the Document class, and make the Document constructor private such that only the Application class can access it. But this makes no sense structurally, and is in fact the opposite of the true relationship. Not to mention that public nested classes are generally discouraged.

The other way I tried was to make the Document constructor private, but give it a public "factory method" that does some checking to try and make sure it really was called by the Application. But this became very convoluted as I tried to implement it.

Am I over-complicating this? Surely it's a common thing for a codebase to be built such that developers could do something that they shouldn't? Should I just expect our developers (and myself) to use discretion and common-sense and not go around constructing new Document objects, when they should be getting them from the Application instead?

Or am I correct in trying to enforce this? Am I correct in my pursuit for classes to only be usable as intended? After all, this is what access levels are for in the first place -- to enforce usage of entities only in the correct context. If this is a reasonable pursuit, does anyone have any suggestions for how to do so in this circumstance?


Note: I found this similar thread: java - make class only instantiable from specific class. But the proposed solution was nested classes, which aren't a good option for the reasons I gave above.
Sigenes
  • 445
  • 2
  • 13
  • 3
    You could make a new interface `IDocument` which is public, then implement it with a *private* inner `Document` class, inside `Application`? – canton7 Mar 13 '20 at 14:25
  • 1
    If they are in the same project, you could make your constructor `internal` – Chronicle Mar 13 '20 at 14:25
  • 2
    You could move these classes into their own project and use `internal`. This can be sidestepped with reflection and `AssemblyInfo` settings but it would be quite clear if someone was constructing them in a way you don't want. – Zer0 Mar 13 '20 at 14:28
  • 1
    The `Application` class shouldn't be calling a constructor anyway - it should be passed an abstract factory method that it uses to do so, although that doesn't help with your issue. However, I think you're trying to overengineer it. – Matthew Watson Mar 13 '20 at 14:28
  • All great suggestions. I'm thinking a public interface/private nested implementation may be the simplest and most appropriate. I'll mull that over for a bit. – Sigenes Mar 13 '20 at 14:55
  • I thought about a separate assembly, but I want to avoid adding extra projects and DLLs. – Sigenes Mar 13 '20 at 14:57
  • @MatthewWatson can you explain why `Application` shouldn't be calling a constructor? Also, is there a design pattern that I can reference for how to do what you're suggesting instead? – Sigenes Mar 13 '20 at 14:58
  • 1
    @DRoam The pattern is called [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection). I can recommend [this book](https://www.manning.com/books/dependency-injection-principles-practices-patterns?a_aid=ploeh&a_bid=844515ef) which is required reading where I work. The [author's blog](https://blog.ploeh.dk/archive/) has some interesting stuff too. – Matthew Watson Mar 13 '20 at 15:17

4 Answers4

2

There is a way to do this, but it's somewhat convoluted and I wouldn't do it...

You can put the Document implementation in a separate assembly and make its constructor internal.

Then put the Application class in another assembly (on its own) and use InternalsVisibleTo in the Document assembly to make the Document constructor visible to the Application assembly.

That way only code in the Application assembly or the Document assembly will be able to construct a Document. (Of course, this can be circumvented simply by adding classes to the Application assembly or the Document assembly that create instances of Document...)

Although I wouldn't actually want the Application class to directly create instances of Document. I'd want to abstract Document behind an IDocument class and pass a factory method to Application to use to create IDocument items.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1

There is no way to do exactly what you want. What you can do is declare all constructors for the class internal/Friend and then only classes in the same assembly will be able to invoke them. It's then up to you which types declared in that assembly actually do invoke those constructors. If you want only one class to do it, only do it in that one class. That's exactly how the DataRow class works. Notice that you cannot create a DataRow object directly but you can do so indirectly, using the DataTable.NewRow or DataRowCollection.Add methods?

jmcilhinney
  • 50,448
  • 5
  • 26
  • 46
0

As far as I know, C# does not provide a way for a class to restrict its methods, in this case a constructor, to only be callable from specific other classes. Nesting Document within Application and exposing it to other classes through an interface may be your best bet -- see this question for an example.

crumb
  • 76
  • 1
  • 6
0

Another option is use

[CallerFilePath] string callerFilePath = ""

as a param in your method (or constructor) to validate if the caller is from your Application. However, this one is still run-time error not compile error.

If you want to enforce at compile time, you need to move both classes into a project and use internal constructor for Document, and public method in Application.

Alex - Tin Le
  • 1,982
  • 1
  • 6
  • 11