Files
spring-net/doc/reference/src/objects-misc.xml

455 lines
18 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<chapter xml:id="objects-misc" xmlns="http://docbook.org/ns/docbook" version="5">
<title>The IObjectWrapper and Type conversion</title>
<sect1 xml:id="objects-misc-introduction">
<title>Introduction</title>
<para>The concepts encapsulated by the
<literal>IObjectWrapper</literal> interface are fundamental to the
workings of the core Spring.NET libraries The typical application
developer most probably will not ever have the need to use the
<literal>IObjectWrapper</literal> directly... because this is
reference documentation however, we felt that some explanation of this
core interface might be right. The <literal>IObjectWrapper</literal>
is explained in this chapter since if you were going to use it at all, you
would probably do that when trying to bind data to objects, which, nicely
enough, is precisely the area that the
<literal>IObjectWrapper</literal> addresses.</para>
</sect1>
<sect1 xml:id="objects-objects">
<title>Manipulating objects using the IObjectWrapper</title>
<para>One quite important concept of the <literal>Spring.Objects</literal>
namespace is encapsulated in the definition
<literal>IObjectWrapper</literal> interface and its corresponding
implementation, the <literal>ObjectWrapper</literal> class. The
functionality offered by the <literal>IObjectWrapper</literal>
includes methods to set and get property values (either individually or in
bulk), get property descriptors (instances of the
<literal>System.Reflection.PropertyInfo</literal> class), and to query
the readability and writability of properties. The
<literal>IObjectWrapper</literal> also offers support for nested
properties, enabling the setting of properties on subproperties to an
unlimited depth. The <literal>IObjectWrapper</literal> usually isn't
used by application code directly, but by framework classes such as the
various <literal>IObjectFactory</literal> implementations.</para>
<para>The way the <literal>IObjectWrapper</literal> works is partly
indicated by its name: <emphasis>it wraps an object</emphasis> to perform
actions on a wrapped object instance... such actions would include the
setting and getting of properties exposed on the wrapped object.</para>
<para><emphasis>Note: the concepts explained in this section are not
important to you if you're not planning to work with the
<literal>IObjectWrapper</literal> directly.</emphasis></para>
<sect2 xml:id="objects-objects-conventions">
<title>Setting and getting basic and nested properties</title>
<para>Setting and getting properties is done using the
<methodname>SetPropertyValue()</methodname> and
<methodname>GetPropertyValue()</methodname> methods, for which there are
a couple of overloaded variants. The details of the various overloads
(including return values and method parameters) are all described in the
extensive API documentation supplied as a part of the Spring.NET
distribution.</para>
<para>The aforementioned <methodname>SetPropertyValue()</methodname> and
<methodname>GetPropertyValue()</methodname> methods have a number of
conventions for indicating the path of a property. A property path is an
expression that implementations of the
<literal>IObjectWrapper</literal> interface can use to look up the
properties of the wrapped object; some examples of property paths
include...</para>
<para><table frame="all">
<title>Examples of property paths</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="4*" />
<thead>
<row>
<entry>Path</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry>name</entry>
<entry>Indicates the <literal>name</literal> property of the
wrapped object.</entry>
</row>
<row>
<entry>account.name</entry>
<entry>Indicates the nested property <literal>name</literal>
of the <literal>account</literal> property of the wrapped
object.</entry>
</row>
<row>
<entry>account[2]</entry>
<entry>Indicates the <emphasis>third</emphasis> element of the
<literal>account</literal> property of the wrapped object.
Indexed properties are typically collections such as
<literal>lists</literal> and <literal>dictionaries</literal>,
but can be any class that exposes an indexer.</entry>
</row>
</tbody>
</tgroup>
</table></para>
<para>Below you'll find some examples of working with the
<literal>IObjectWrapper</literal> to get and set properties.
Consider the following two classes: <programlisting language="csharp">[C#]
public class Company
{
private string name;
private Employee managingDirector;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public Employee ManagingDirector
{
get { return this.managingDirector; }
set { this.managingDirector = value; }
}
}</programlisting> <programlisting language="csharp">[C#]
public class Employee
{
private string name;
private float salary;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public float Salary
{
get { return salary; }
set { this.salary = value; }
}
}</programlisting></para>
<para>The following code snippets show some examples of how to retrieve
and manipulate some of the properties of
<literal>IObjectWrapper</literal>-wrapped <literal>Company</literal>
and <literal>Employee</literal> instances. <programlisting language="csharp">[C#]
Company c = new Company();
IObjectWrapper owComp = new ObjectWrapper(c);
// setting the company name...
owComp.SetPropertyValue("name", "Salina Inc.");
// can also be done like this...
PropertyValue v = new PropertyValue("name", "Salina Inc.");
owComp.SetPropertyValue(v);
// ok, let's create the director and bind it to the company...
Employee don = new Employee();
IObjectWrapper owDon = new ObjectWrapper(don);
owDon.SetPropertyValue("name", "Don Fabrizio");
owComp.SetPropertyValue("managingDirector", don);
// retrieving the salary of the ManagingDirector through the company
float salary = (float)owComp.GetPropertyValue("managingDirector.salary");</programlisting></para>
<para>Note that since the various Spring.NET libraries are compliant
with the Common Language Specification (CLS), the resolution of
arbitrary strings to properties, events, classes and such is performed
in a case-insensitive fashion. The previous examples were all written in
the C# language, which is a case-sensitive language, and yet the
<literal>Name</literal> property of the <literal>Employee</literal>
class was set using the all-lowercase <literal>'name'</literal> string
identifier. The following example (using the classes defined previously)
should serve to illustrate this...</para>
<programlisting language="csharp">[C#]
// ok, let's create the director and bind it to the company...
Employee don = new Employee();
IObjectWrapper owDon = new ObjectWrapper(don);
owDon.SetPropertyValue("naMe", "Don Fabrizio");
owDon.GetPropertyValue("nAmE"); // gets "Don Fabrizio"
IObjectWrapper owComp = new ObjectWrapper(new Company());
owComp.SetPropertyValue("ManaGINGdirecToR", don);
owComp.SetPropertyValue("mANaGiNgdirector.salARY", 80000);
Console.WriteLine(don.Salary); // puts 80000</programlisting>
<para>The case-insensitivity of the various Spring.NET libraries
(dictated by the CLS) is not usually an issue... if you happen to have a
class that has a number of properties, events, or methods that differ
only by their case, then you might want to consider refactoring your
code, since this is generally regarded as poor programming
practice.</para>
</sect2>
<sect2 xml:id="objects-objects-other">
<title>Other features worth mentioning</title>
<para>In addition to the features described in the preceding sections
there a number of features that might be interesting to you, though not
worth an entire section. <itemizedlist spacing="compact">
<listitem>
<para><emphasis>determining readability and
writability</emphasis>: using the <literal>IsReadable()</literal>
and <literal>IsWritable()</literal> methods, you can determine
whether or not a property is readable or writable.</para>
</listitem>
<listitem>
<para><emphasis>retrieving PropertyInfo instances</emphasis>:
using <literal>GetPropertyInfo(string)</literal> and
<literal>GetPropertyInfos()</literal> you can retrieve instances
of the <literal>System.Reflection.PropertyInfo</literal>
class, that might come in handy sometimes when you need access to
the property metadata specific to the object being wrapped.</para>
</listitem>
</itemizedlist></para>
</sect2>
</sect1>
<sect1 xml:id="objects-objects-conversion">
<title>Type conversion</title>
<para>If you associate a <literal>TypeConverter</literal> with the
definition of a custom <literal>Type</literal> using the standard .NET
mechanism (see the example code below), Spring.NET will use the associated
<literal>TypeConverter</literal> to do the conversion.<programlisting language="csharp">[C#]
[TypeConverter (typeof (FooTypeConverter))]
public class Foo
{
}</programlisting></para>
<para>The <literal>TypeConverter</literal> class from the
<literal>System.ComponentModel</literal> namespace of the .NET BCL is used
extensively by the various classes in the <literal>Spring.Core</literal>
library, as said class <quote>... provides a unified way of converting
types of values to other types, as well as for accessing standard values
and subproperties.</quote> <footnote>
<para>More information about creating custom
<literal>TypeConverter</literal> implementations can be found online
at Microsoft's MSDN website, by searching for <emphasis>Implementing a
Type Converter</emphasis>.</para>
</footnote></para>
<para>For example, a date can be represented in a human readable format
(such as <literal>30th August 1984</literal>), while we're still able to
convert the human readable form to the original date format or (even
better) to an instance of the <literal>System.DateTime</literal>
class. This behavior can be achieved by using the standard .NET idiom of
decorating a class with the <literal>TypeConverterAttribute</literal>.
Spring.NET also offers another means of associating a
<literal>TypeConverters</literal> with a class. You might want to do
this to achieve a conversion that is not possible using standard idiom...
for example, the <literal>Spring.Core</literal> library contains a custom
<literal>TypeConverter</literal> that converts comma-delimited strings
to String array instances. Registering custom converters on an
<literal>IObjectWrapper</literal> instance gives the wrapper the
knowledge of how to convert properties to the desired
<literal>Type</literal>.</para>
<para>An example of where property conversion is used in Spring.NET is the
setting of properties on objects, accomplished using the aforementioned
<literal>TypeConverters</literal>. When mentioning
<literal>System.String</literal> as the value of a property of some
object (declared in an XML file for instance), Spring.NET will (if the
type of the associated property is <literal>System.Type</literal>) use
the <literal>RuntimeTypeConverter</literal> class to try to resolve
the property value to a <literal>Type</literal> object. The example
below demonstrates this automatic conversion of the
<literal>Example.Xml.SAXParser</literal> (a string) into the corresponding
<literal>Type</literal> instance for use in this factory-style class.
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="parserFactory" type="Example.XmlParserFactory, ExamplesLibrary"
destroy-method="Close"&gt;
&lt;property name="ParserClass" value="Example.Xml.SAXParser, ExamplesLibrary"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting> <programlisting language="csharp">[C#]
public class XmlParserFactory
{
private Type parserClass;
public Type ParserClass
{
get { return this.parserClass; }
set { this.parserClass = value; }
}
public XmlParser GetParser ()
{
return Activator.CreateInstance (ParserClass);
}
}</programlisting></para>
<sect2 xml:id="objects-misc-enums">
<title>Type Conversion for Enumerations</title>
<para>The default type converter for enumerations is the
<literal>System.ComponentModel.EnumConverter</literal> class. To
specify the value for an enumerated property, simply use the name of the
property. For example the <literal>TestObject</literal> class has a
property of the enumerated type <literal>FileMode</literal>. One of
the values for this enumeration is named <literal>Create</literal>. The
following XML fragment shows how to configure this property</para>
<programlisting language="myxml">&lt;object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"&gt;
&lt;property name="name" value="Rod"/&gt;
&lt;property name="FileMode" value="Create"/&gt;
&lt;/object&gt;</programlisting>
</sect2>
</sect1>
<sect1 xml:id="object-objects-builtin-converters">
<title>Built-in TypeConverters</title>
<para>Spring.NET has a number of built-in
<literal>TypeConverters</literal> to make life easy. Each of those is
listed below and they are all located in the
<literal>Spring.Objects.TypeConverters</literal> namespace of the
<literal>Spring.Core</literal> library.</para>
<para><table frame="all">
<title>Built-in <literal>TypeConverters</literal></title>
<tgroup cols="2">
<colspec colname="c1" colwidth="3*" />
<colspec colname="c2" colwidth="5*" />
<thead>
<row>
<entry>Type</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>RuntimeTypeConverter</literal></entry>
<entry>Parses strings representing
<literal>System.Types</literal> to actual
<literal>System.Types</literal> and the other way
around.</entry>
</row>
<row>
<entry><literal>FileInfoConverter</literal></entry>
<entry>Capable of resolving strings to a
<literal>System.IO.FileInfo</literal> object.</entry>
</row>
<row>
<entry><literal>StringArrayConverter</literal></entry>
<entry>Capable of resolving a comma-delimited list of strings to
a string-array and vice versa.</entry>
</row>
<row>
<entry><literal>UriConverter</literal></entry>
<entry>Capable of resolving a string representation of a URI to
an actual <literal>Uri</literal>-object.</entry>
</row>
<row>
<entry><literal>FileInfoConverter</literal></entry>
<entry>Capable of resolving a string representation of a
FileInfo to an actual
<literal>FileInfo</literal>-object.</entry>
</row>
<row>
<entry><literal>StreamConverter</literal></entry>
<entry>Capable of resolving Spring IResource URI (string) to its
corresponding <literal>InputStream</literal>-object.</entry>
</row>
<row>
<entry><literal>ResourceConverter</literal></entry>
<entry>Capable of resolving Spring IResource URI (string) to an
<literal>IResource</literal> object.</entry>
</row>
<row>
<entry><literal>ResourceManagerConverter</literal></entry>
<entry>Capable of resolving a two part string (resource name,
assembly name) to a
<literal>System.Resources.ResourceManager</literal>
object.</entry>
</row>
<row>
<entry><literal>RgbColorConverter</literal></entry>
<entry>Capable of resolving a comma separated list of Red,
Green, Blue integer values to a
<literal>System.Drawing.Color</literal> structure.</entry>
</row>
<row>
<entry>RegexConverter</entry>
<entry>Converts string representation of regular expression into
an instance of System.Text.RegularExpressions.Regex</entry>
</row>
</tbody>
</tgroup>
</table></para>
<para>Spring.NET uses the standard .NET mechanisms for the resolution of
<literal>System.Types</literal>, including, but not limited to
checking any configuration files associated with your application,
checking the Global Assembly Cache (GAC), and assembly probing.</para>
<sect2>
<title>Custom type converters</title>
<para>You can register a custom type converter either Programatically
using the class TypeConverterRegistry or through configuration of
Spring's container and described in the section <link
linkend="context-type-converters">Registering Type
Converters</link>.</para>
<para></para>
</sect2>
</sect1>
</chapter>