Reputation: 13025
I'm writing a DLL using C# 4.0 which will be used by several C# .NET based desktop applications (let's call them AppA and AppB). It is required that AppA will be able to use certain class' selected fields/properties/functions that won't be even available to AppB. My approach was to use internal
modifier for those properties and grant access to AppA by specifying AppA's assembly name in InternalsVisibleTo
attribute of the DLL. But internal
modifier is also required in some properties which will be accessed in other parts of the DLL but not to be accessed by AppA. Now it appears that there are too many internal
s which are exposed to AppA which shouldn't be accessible by AppA.
In other words, consider the following properties:
class A
{
internal int ReallyInternal {get; set;}
internal int AppAInternal {get; set;}
}
If I use InternalsVisibleTo
attribute for AppA, then both ReallyInternal
and AppAInternal
properties will be exposed to AppA - where as ReallyInternal
shouldn't be exposed to AppA.
How to solve this? Is there any other way to implement this scenario?
Background
Before going to InternalsVisibleTo
approach we thought of other ways, like having different interfaces etc. The class library I'm writing will be used by multiple applications. I wanted to have the interface same across applications.
Consider TT4
as a class in the DLL. It's properties will be populated from a physical device via serial communication.
TT4 tt4 = new TT4();
// Some code to populate tt4 object
MessageBox.Show(tt4.SerialNumber);
tt4.SerialNumber = "123";
Because tt4
object will represent a physical device, not all of its properties should be modifiable by all applications. This won't make sense and if we allow this then any application can change the serial number of the device. (Yes, SerialNumber can be written back to the device).
We've only one application (here AppA for example) which will be able to set and change the SerialNumber. Other applications shouldn't do that. It's prevented by making SerialNumber's setter as internal
and granting permission to AppA by InternalsVisibleTo
.
Please note that providing the library with two classes is not the solution. Say, I've implemented two classes for TT4
- TT4
(cannot write SerialNumber) and TT4Super
(can write SerialNumber). When the DLL will be given to clients, they can still see TT4Super
and use that.
I'm not the developer of other applications and I have no control of them.
Upvotes: 0
Views: 91
Reputation: 101700
Are you mostly concerned that the designers of AppB are going to do something malicious with your DLL, or that you simply want to prevent them from doing something inadvertently? Making your members internal
won't really prevent someone from doing harm via reflection if they want to.
One (admittedly not great) approach you could use is to make these members public, but prevent anyone except AppA from using them, by checking the calling assembly:
private void VerifyCaller(Assembly a)
{
if (a == Assembly.GetExecutingAssembly()) { return; }
var name = a.GetName();
if(name.Name == "AppA" && name.GetPublicKey() == appAPublicKey) { return; }
throw new InvalidOperationException("You can't access this");
}
private string _serialNumber;
public string SerialNumber
{
get { return _serialNumber; }
set
{
VerifyCaller(Assembly.GetCallingAssembly());
_serialNumber = value;
}
}
this should at least prevent anyone from easily using reflection to circumvent your defenses.
Upvotes: 1
Reputation: 8782
One way to do that would be to extract all the members that should be exposed to AppA to an abstract class (or a parent class in general) and make them protected internal
. To access them from AppA it would have to inherit from the abstract class. For instance
public abstract class ParentA
{
internal int ReallyInternal {get; set;}
protected internal int AppAInternal {get; set;}
}
And AppA accesses it the following way:
internal class AinAppA : ParentA
{
internal AinAppA()
{
this.AppAInternal = 1; // can access parents protected members
// this.ReallyInternal = 2; // but pure internal members are not visible
}
}
As a side note, InternalsVisibleTo
is not meant to be an access modifier. It's main purpose is to make unit testing easier not to enable communication between production assemblies.
Upvotes: 2