简体   繁体   中英

Restricting use of a structure in C#

Ok so lets say I have a structure A like that:

Struct A{
    private String _SomeText;
    private int _SomeValue;

    public A(String someText, int SomeValue) { /*.. set the initial values..*/ }

    public String SomeText{ get { return _SomeText; } }
    public int SomeValue{ get { return _SomeValue; } }
}

Now what I want to be able to do is to return that Structure A as a result of a method in a Class ABC, like that:

Class ABC{
    public A getStructA(){
        //creation of Struct A
        return a;
    }
}

I don't want any programmer using my library (which will have Struct A and Class ABC and some more stuff) to ever be able to create an instance of Struct A.
I want the only way for it to be created is as a return from the getStructA() method. Then the values can be accessed through the appropriate getters.

So is there any way to set a restrictions like that? So a Structure can't be instantiated outside of a certain class? Using C#, .Net4.0.

Thanks for your help.

---EDIT:----
To add some details on why am I trying to achieve this:

  • My class ABC has some "status" a person can query. This status has 2 string values and then a long list of integers.
  • There never will be a need to create an object/instance of "Status" by the programmer, the status can only be returned by "getStatus()" function of the class.
  • I do not want to split these 3 fields to different methods, as to obtain them I am calling Windows API (p/invoke) which returns similar struct with all 3 fields.
  • If I was indeed going to split it to 3 methods and not use the struct, I would have to either cache results or call the method from Windows API every time one of these 3 methods is called...

So I can either make a public struct and programmers can instantiate it if they want, which will be useless for them as there will be no methods which can accept it as a parameter. Or I can construct the library in such a way that this struct (or change it to a class if it makes things easier) can be obtained only as a return from the method.

If the "restricted" type is a struct, then no, there is no way to do that. The struct must be at least as public as the factory method, and if the struct is public then it can be constructed with its default constructor. However, you can do this:

public struct A
{
    private string s;
    private int i;
    internal bool valid;
    internal A(string s, int i)
    {
        this.s = s;
        this.i = i;
        this.valid = true;
    } 
    ...

and now you can have your library code check the "valid" flag. Instances of A can only be made either (1) by a method internal to your library that can call the internal constructor, or (2) by the default constructor. You can tell them apart with the valid flag.

A number of people have suggested using an interface, but that's a bit pointless; the whole point of using a struct is to get value type semantics and then you go boxing it into an interface. You might as well make it a class in the first place. If it is going to be a class then it is certainly possible to make a factory method; just make all the ctors of the class internal.

And of course I hope it goes without saying that none of this gear should be used to implement code that is resistant to attack by a fully-trusted user. Remember, this system is in place to protect good users from bad code , not good code from bad users . There is nothing whatsoever that stops fully trusted user code from calling whatever private methods they want in your library via reflection, or for that matter, altering the bits inside a struct with unsafe code.

Create a public interface and make the class private to the class invoking it.

public ISpecialReturnType
{
    String SomeText{ get; }
    int SomeValue{ get; }
}

class ABC{
    public ISpecialReturnType getStructA(){
        A a = //Get a value for a;
        return a;
    }

    private struct A : ISpecialReturnType
    {
        private String _SomeText;
        private int _SomeValue;

        public A(String someText, int SomeValue) { /*.. set the initial values..*/ }

        public String SomeText{ get { return _SomeText; } }
        public int SomeValue{ get { return _SomeValue; } }
    }
}

What exactly are you concerned about? A structure is fundamentally a collection of fields stuck together with duct tape. Since struct assignment copies all of the fields from one struct instance to another, outside the control of the struct type in question, structs have a very limited ability to enforce any sort of invariants, especially in multi-threaded code (unless a struct is exactly 1, 2, or 4 bytes, code that wants to create an instance which contains a mix of data copied from two different instances may do so pretty easily, and there's no way the struct can prevent it).

If you want to ensure that your methods will not accept any instances of a type other than those which your type has produced internally, you should use a class that either has only internal or private constructors. If you do that, you can be certain that you're getting the instances that you yourself produced.

Based upon the revisions, I don't think the requested type of restriction is necessary or particularly helpful. 根据修订版,我不认为所要求的限制类型是必要的或特别有用。 It sounds like what's fundamentally desired to stick a bunch of values together and store them into a stuck-together group of variables held by the caller. If you declare a struct as simply:

public struct QueryResult {
  public ExecutionDuration as Timespan;
  public CompletionTime as DateTime;
  public ReturnedMessage as String;
}

then a declaration:

QueryResult foo;

will effectively create three variables, named foo.ExecutionDuration , foo.CompletionTime , and foo.ReturnedMessage . The statement:

foo = queryPerformer.performQuery(...);

will set the values of those three variables according to the results of the function--essentially equivalent to:

{
  var temp = queryPerformer.performQuery(...);
  foo.ExecutionDuration = temp.ExecutionDuration
  foo.CompletionTime = temp.CompletionTime;
  foo.ReturnedMessage = temp.ReturnedMessage;
}

Nothing will prevent user code from doing whatever it wants with those three variables, but so what? If user code decides for whatever reason to say foo.ReturnedMessage = "George"; then foo.ReturnedMessage will equal George . The situation is really no different from if code had said:

int functionResult = doSomething();

and then later said functionResult = 43; . The behavior of functionResult , like any other variable, is to hold the last thing written to it. If the last thing written to it is the result of the last call to doSomething() , that's what it will hold. If the last thing written was something else, it will hold something else.

Note that a struct field, unlike a class field or a struct property, can only be changed either by writing to it, or by using a struct assignment statement to write all of the fields in one struct instance with the values in corresponding fields of another. From the consumer's perspective, a read-only struct property carries no such guarantee. A struct may happen to implement a property to behave that way, but without inspecting the code of the property there's no way to know whether the value it returns might be affected by some mutable object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM