views:

232

answers:

5

I have a public class I'm defining that will eventually be part of an API, so it has to have certain public properties. However, I also want some of the properties to be read-only unless they're created from within my own projects (e.g., if a user has our API, they can create a User object, but they can't write to its ID field, which will be filled in only if we pull it from the database).

My thought was to have two separate assemblies using the same namespace, the "public" DLL and the "private" DLL. The prototype in the public DLL will look like:

namespace CompanyName
{
  public partial class User
  {
 public Id { get; }
 public Name { get; set; }
  }
}

and the private DLL will have this:

namespace CompanyName
{
  public partial class User
  {
 public Id { set; }
  }
}

Would this work? If not, what's a better way to do this?

+9  A: 

Partial classes cannot span assemblies, so this will not work.

You could define your class as:

namespace CompanyName
{
    public class User
    {
        public Id {get;internal set;}
        public Name {get;set;}
    }
}

This would mean that only code with internal access to your class could set the value of Id property. If you need to set it from outside your assembly, make sure your assemblies are strong-named and you can then use the InternalsVisibleTo attribute to give internal access to your assembly to another one of your assemblies (the one setting the value of Id).


I've recently had to do something very similar to this for an API I work on. Our API is defined mainly using interfaces, so I was able to achieve this by having a Public API project that is the public part, and an Internal API project that forms the API used by our internal code, with internal interfaces deriving from the public ones. The implementations of the API interfaces implement both interfaces, meaning our internal code can then access parts of the API that are not public.

adrianbanks
i never knew u could make anything internal...
Daniel A. White
I like the internal interfaces extending public ones idea... especially if its done explicitly, since (if I'm not mistaken) there is no way to call the methods, even with reflection, without having a reference to the interface type...
LorenVS
Yes. The only drawback I've come across is that you have to do ((IInternalInterface) interface) to get the internal interface - but then it does make it explicit that you are using internal-only functionality.
adrianbanks
+2  A: 

I doubt this would work. I would imagine partial classes are compiled together into the same assembly and not handled by the CLR. You might want to see the internal keyword.

Maybe do something like this

abstract internal class UserPrototype
{
    protected Property....
}


sealed class User : UserPrototype
{
    public ...
}
Daniel A. White
+5  A: 

No, this wouldn't work. Partial classes are merged at compile time: you can't add members to a compiled class.

Depending on exactly how your code is laid out, a better approach is to provide an internal setter:

public int Id { get; internal set; }

If you need to be able to do the set from another assembly, but only one you control, you can use InternalsVisibleToAttribute to grant that assembly access to the internal setter.

itowlson
InternalsVisibleToAttribute may be what I'm looking for. Thanks!
Adam V
A: 

Even if you could do this, your private properties and fields will still be discoverable via reflection. From the MSDN page for GetProperty:

The following BindingFlags filter flags can be used to define which properties to include in the search:

You must specify either BindingFlags.Instance or BindingFlags.Static in order to get a return.

Specify BindingFlags.Public to include public properties in the search.

Specify BindingFlags.NonPublic to include non-public properties (that is, private and protected properties) in the search.

ChrisF
True, but there is a difference of being able to set the value of the Id just by consuming the API and using Intellisense, and actively "hacking" the use of the API using reflection.
adrianbanks
A: 

The trick with designing APIs is to think in categories of interfaces (int this case - abstract classes). Please have a look at this code:

public abstract class User
{
    protected String _name;
}

public sealed class PublicUser : User
{
    public String Name
    {
        get{ return this._name; }
    }
}

public class PrivateUser : User
{
    public String Name
    {
        get { return this._name; }
        set { this._name = value; }
    }
}

Obviously you can use any class/namespace names, this is just for making things clear. All classes are - as you can see - public, so it's up to you now which DLL will be available for your client.

Piotr Justyna