Objective C # principle 23: avoid returning references to internal class objects)

Source: Internet
Author: User

Objective C # principle 23: avoid returning references to internal class objects

Item 23: avoid returning references to internal class objects

As you know, the so-called read-only attribute means that the caller cannot modify this attribute. Unfortunately, this is not always effective. If you create an attribute and return a reference type, the caller can access the public members of the object and modify the status of these attributes. For example:

Public class mybusinessobject
{
// Read only property providing access to
// Private Data member:
Private dataset _ DS;
Public dataset data
{
Get
{
Return _ DS;
}
}
}

// Access the dataset:
Dataset DS = bizobj. Data;
// Not intended, but allowed:
DS. Tables. Clear (); // deletes all data tables.

Any public customer of mybusinessobject can modify your internal dateset. The attributes you create are used to hide the internal data structure of the class. You provide methods to familiarize customers with the operation data of this method. Therefore, your class can manage any changes to the internal status. However, the read-only attribute opens a backdoor for class encapsulation. When you consider these issues, it is not a readable and writable attribute, but a read-only attribute.

Welcome to a wonderful reference-based system. Any member who returns the reference will return a handle to the object. You have given the caller an interface handle. Therefore, when the caller modifies an internal reference of this object, the caller no longer needs to use this object.

It is clear that you want to prevent such a thing from happening. You have created an interface for your class and want the user to use it. You do not want users to access and modify the internal status of objects when they do not understand your intentions. You have four policies to protect your internal data structure from unintentional modification: Value Type, constant type, interface and packaging (mode ).

When the value type is accessed through the attribute, it is a copy of the data. Any modification made by the customer to copy data of the class does not affect the internal status of the object. You can modify the copied data as needed. This has no influence on your internal status.

Constant type, such as system. String, is also safe. You can return a string or other constant type. Constant type security tells you that no customer can modify strings. Your internal status is secure.

The third option is to define interfaces to allow customers to access some of the internal members' functions (see Principle 19 ). When you create a class of your own, you can create some setting interfaces to support setting sub-objects of the class. Using these interfaces to expose some function functions, You can minimize unintentional changes to the data. Customers can use the internal objects of the callback class through the interface you provide, and this interface does not contain all the functions of this class. This policy is used to expose an ilistsource interface on dataset, which can block some ideas.ProgramOperator to guess the object implementing this interface and force conversion. In this way, the programmer pays more work and finds that more bugs are self-seeking. Readers can refer to the original article by themselves: but programmers who go to that much work to create bugs get what they deserve .).

The system. dataset class also uses the last policy: Wrap the object. The dataviewmanager class provides a method to access dataset, and prevents the redirection method from accessing the dataseto class:

Public class mybusinessobject
{
// Read only property providing access to
// Private Data member:
Private dataset _ DS;
Public dataview this [String tablename]
{
Get
{
Return _ DS. defaultviewmanager.
Createdataview (_ DS. Tables [tablename]);
}
}
}

// Access the dataset:
Dataview list = bizobj ["MERs"];
Foreach (datarowview R in List)
Console. writeline (R ["name"]);

Dataviewmanager creates dataview to access individual data tables in dataset. Dataviewmanager does not provide any method to modify the data table in the dataset. Each dataview can be configured as a license to modify individual data elements, but the customer cannot modify the data table or columns of the data table. Read/write operations are performed by default. Therefore, you can add, modify, or delete individual data entries.

Before we begin to discuss how to create a fully read-only data view, let me first briefly understand how you should respond to changes made by public users. This is very important because you may often expose a dataview to the UI control so that users can edit the data (see Principle 38 ). Make sure that you have used Windows form data binding to edit private data of objects. The datatable in dataset triggers some events, so that you can easily implement the viewer mode: Your class can respond to any modifications made by other customers. The able object in dataset triggers an event when any column or row in the data table changes. Columnchanging and rowchanging events are raised before the edited data is submitted to dataset. The columnchanged and rowchanged events are triggered after the modifications are submitted.

This technology can be extended at any time when you want to provide public customers with methods to modify internal data, but you need to verify and respond to these changes. Your class should describe the events generated by the internal data structure. The event handle verifies and responds to changes by updating these internal states.

Back to the original issue, you want the customer to view your data, but do not make any changes. When your data is stored in a dataset, you can force a dataview to be created on the datatable to prevent any modifications. The dataview class contains some attributes. By defining these attributes, dataview can be added, deleted, modified, or even sorted on the actual table. You can use the indexer on the requested able and create an indexer to return a custom dataview:

Public class mybusinessobject
{
// Read only property providing access to
// Private Data member:
Private dataset _ DS;
Public ilist this [String tablename]
{
Get
{
Dataview view =
_ DS. defaultviewmanager. createdataview
(_ DS. Tables [tablename]);
View. allownew = false;
View. allowdelete = false;
View. allowedit = false;
Return view;
}
}
}

// Access the dataset:
Ilist DV = bizojb ["MERs"];
Foreach (datarowview R in DV)
Console. writeline (R ["name"]);

The last excerpt of this class (CodeBy accessing the ilist interface reference, the view on the actual data table is returned. You can use the ilist interface on any set, not limited to dataset. You should not simply return the dataview object. You can easily get the edit, add, and delete capabilities again. The view you returned is already custom and cannot be modified on the objects in the list. The returned ilist pointer ensures that the customer does not have the modification rights granted to the dataview object.

The reference type exposed to the user from the public interface allows the user to modify the internal members of the object without accessing the object. This seems incredible, and may cause some errors. You need to modify the class interface and reconsider that you are exposing a reference instead of a value type. If you simply return internal data, you give others the opportunity to access internal members. Your customer can call any available methods on the member. You can restrict some internal private data access or wrap objects by exposing interfaces. When you want your customers to modify your internal data, you should implement your own observer mode so that your objects can verify and modify or respond to them.
======================================

Item 23: avoid returning references to internal class objects
You 'd like to think that a read-only property is read-only and that callers can't modify it. unfortunately, that's not always the way it works. if you create a property that returns a reference type, the caller can access any public member of that object, including those that modify the state of the property. for example:

Public class mybusinessobject
{
// Read only property providing access to
// Private Data member:
Private dataset _ DS;
Public dataset data
{
Get
{
Return _ DS;
}
}
}

// Access the dataset:
Dataset DS = bizobj. Data;
// Not intended, but allowed:
DS. Tables. Clear (); // deletes all data tables.

 

Any public client of mybusinessobject can modify your internal dataset. you created properties to hide your internal data structures. you provide methods to let clients manipulate the data only through known methods, so your class can manage any changes to internal state. and then a read-only property opens a gaping hole in your class encapsulation. it's not a read-write property, where you wowould consider these issues, but a read-only property.

Welcome to the wonderful world of reference-based systems. any member that returns a reference type returns a handle to that object. you gave the caller a handle to your internal structures, so the caller no longer needs to go through your object to modify that contained reference.

Clearly, you want to prevent this kind of behavior. you built the interface to your class, and you want users to follow it. you don't want users to access or modify the internal state of your objects without your knowledge. you 've got four different strategies for protecting your internal data structures from unintended modifications: value types, immutable types, interfaces, and wrappers.

Value types are copied when clients access them through a property. any changes to the copy retrieved by the clients of your class do not affect your object's internal state. clients can change the copy as much as necessary to achieve their purpose. this does not affect your internal state.

Immutable types, such as system. string, are also safe. you can return strings, or any immutable type, safely knowing that no client of your class can modify the string. your internal state is safe.

the third option is to define interfaces that allow clients to access a subset of your internal Member's functionality (see item 19 ). when you create your own classes, you can create sets of interfaces that support subsets of the functionality of your class. by exposing the functionality through those interfaces, you minimize the possibility that your internal data changes in ways you did not intend. clients can access the internal object through the interface you supplied, which will not include the full functionality of the class. exposing the ilistsource interface pointer in the dataset is one example of this strategy. the Machiavellian programmers out there can defeat that by guessing the type of the object that implements the interface and using a cast. but programmers who go to that much work to create bugs get what they deserve.

The system. dataset class also uses the last strategy: wrapper objects. The dataviewmanager class provides a way to access the dataset but prevents the Mutator methods available through the dataset class:

Public class mybusinessobject
{
// Read only property providing access to
// Private Data member:
Private dataset _ DS;
Public dataview this [String tablename]
{
Get
{
Return _ DS. defaultviewmanager.
Createdataview (_ DS. Tables [tablename]);
}
}
}

// Access the dataset:
Dataview list = bizobj ["MERs"];
Foreach (datarowview R in List)
Console. writeline (R ["name"]);

 

the dataviewmanager creates dataviews to access individual data tables in the dataset. there is no way for the user of your class to modify the tables in your dataset through the dataviewmanager. each dataview can be configured to allow the modification of individual data elements. but the client cannot change the tables or columns of data. read/write is the default, so clients can still add, modify, or delete individual items.

before we talk about how to create a completely read-only view of the data, let's take a brief look at how you can respond to changes in your data when you allow public clients to modify it. this is important because you'll often want to export a dataview to UI controls so that the user can edit the data (see item 38 ). you 've undoubtedly already used Windows Forms Data Binding to provide the means for your users to edit private data in your objects. the datatable class, inside the dataset, raises events that make it easy to implement the observer pattern: Your classes can respond to any changes that other clients of your class have made. the datatable objects inside your dataset will raise events when any column or row changes in that table. the columnchanging and rowchanging events are raised before an edit is committed to the datatable. the columnchanged and rowchanged events are raised after the change is committed.

You can generalize this technique anytime you want to expose internal data elements for modification by public clients, but you need to validate and respond to those changes. your class subscribes to events generated by your internal data structure. event Handlers validate changes or respond to those changes by updating other internal state.

Going back to the original problem, you want to let clients view your data but not make any changes. when your data is stored in a dataset, You can enforce that by creating a dataview for a table that does not allow any changes. the dataview class contains properties that let you customize support for add, delete, modification, or even sorting of the participating table. you can create an indexer to return a customized dataview on the requested table using an indexer:

Public class mybusinessobject
{
// Read only property providing access to
// Private Data member:
Private dataset _ DS;
Public ilist this [String tablename]
{
Get
{
Dataview view =
_ DS. defaultviewmanager. createdataview
(_ DS. Tables [tablename]);
View. allownew = false;
View. allowdelete = false;
View. allowedit = false;
Return view;
}
}
}

// Access the dataset:
Ilist DV = bizojb ["MERs"];
Foreach (datarowview R in DV)
Console. writeline (R ["name"]);

 

this final Excerpt of the class returns the view into a special data table through its ilist interface reference. you can use the ilist interface with any collection; it's not specific to the dataset. you shoshould not simply return the dataview object. users cocould easily enable the editing and add/delete capability again. the view you are returning has been customized to disallow any modifications to the objects in the list. returning the ilist pointer keeps clients from modifying the rights they have been given to the dataview object.

exposing reference types through your public interface allows users of your object to modify its internals without going through the methods and properties you 've defined. that seems counterintuitive, which makes it a common mistake. you need to modify your class's interfaces to take into account that you are exporting references rather than values. if you simply return internal data, you 've given access to those contained members. your clients can call any method that is available in your members. you limit that access by exposing private internal data using interfaces, or wrapper objects. when you do want your clients to modify your internal data elements, you shocould implement the observer pattern so that your objects can validate changes or respond to them.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.