.NET - how to make a class such that only one othe

2020-08-21 03:48发布

I'd like to have the following setup:

class Descriptor
{
    public string Name { get; private set; }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection

    private Descrtiptor() { }
    public Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
}

class Parameter
{
    public string Name { get; private set; }
    public string Valuie { get; private set; }
}

The whole structure will be read-only once loaded from an XML file. I'd like to make it so, that only the Descriptor class can instantiate a Parameter.

One way to do this would be to make an IParameter interface and then make Parameter class private in the Descriptor class, but in real-world usage the Parameter will have several properties, and I'd like to avoid redefining them twice.

Is this somehow possible?

6条回答
Root(大扎)
2楼-- · 2020-08-21 04:21

You could use a constructor marked Internal.

That way it's public to classes in the assembly, and private to classes outside of it.

查看更多
Root(大扎)
3楼-- · 2020-08-21 04:23

LBushkin has the right idea. If you want to avoid having to retype all the properties just right-click the name of the class and choose "Refactor" > "Extract Interface", that should give you an interface that contains all those properties. (This works in VS 2008, I don't know about earlier versions.)

C# generally takes the approach that instead of avoiding redundant code, VS will just help you write it faster.

查看更多
仙女界的扛把子
4楼-- · 2020-08-21 04:30

If you want only the Descriptor class to instantiate a Parameter, then you can make the Descriptor class a nested class of Parameter. (NOT the other way around) This is counterintuitive as the container or parent class is the nested class.

public class Parameter  
{  
  private Parameter() { }
  public string Name { get; private set; }
  public string Value { get; private set; }
  public static Parameter.Descriptor GetDescriptorByName(string Name)
  {
    return Parameter.Descriptor.GetByName(Name);
  }
  public class Descriptor
  { // Only class with access to private Parameter constructor
    private Descriptor() { // Initialize Parameters }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection
    public string Name { get; private set; }
    public static Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
  }  
}
查看更多
我欲成王,谁敢阻挡
5楼-- · 2020-08-21 04:33

Mark the class to be "protected" from instantiation (Parameter) with the StrongNameIdentityPermission attribute and the SecurityAction.LinkDemand option:

[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey="...")]
class Parameter
{
    ...
}

You will need to provide the appropriate public key. Because you are demanding a link-time (JIT-time, in fact) check on the Parameterclass, this means that it can only be used from an assembly that is signed with a strong name that uses the private key matching the public key that you supply in the attribute constructor above. Of course, you will need to put the Descriptor class in a separate assembly and give it a strong name accordingly.

I have used this technique in a couple of applications and it worked very well.

Hope this helps.

查看更多
看我几分像从前
6楼-- · 2020-08-21 04:38

Make it a private nested class that implements a particular interface. Then, only the outer class can instantiate it, but anyone can consume it (through the interface). Example:

interface IParameter
{ 
    string Name { get; } 
    string Value { get; }
}

class Descriptor
{
    public string Name { get; private set; }
    public IList<IParameter> Parameters { get; private set; }

    private Descriptor() { }
    public Descriptor GetByName(string Name) { ... }

    class Parameter : IParameter
    {
        public string Name { get; private set; }
        public string Value { get; private set; }
    }
}

If you really must avoid the interface, you can create a public abstract class that has all of the properties but declares a protected constructor. You can then create a private nested class that inherits from the public abstract that can only be created by the outer class and return instances of it as the base type. Example:

public abstract AbstractParameter
{ 
    public string Name { get; protected set; } 
    public string Value { get; protected set; }
}

class Descriptor
{
    public string Name { get; private set; }
    public IList<AbstractParameter> Parameters { get; private set; }

    private Descriptor() { }
    public Descriptor GetByName(string Name) { ... }

    private class NestedParameter : AbstractParameter
    {
        public NestedParameter() { /* whatever goes here */ }
    }
}
查看更多
smile是对你的礼貌
7楼-- · 2020-08-21 04:39

There is another way: check the call stack for the calling type.

查看更多
登录 后发表回答