Nullable types in ActionScript

View: New views
1 Messages — Rating Filter:   Alert me  

Nullable types in ActionScript

by Tim Clem-2 :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

We're trying to have near-seamless support for nullable types in
ActionScript, but we've hit a roadblock. Has anyone else solved this
problem in an elegant way? We have four goals in mind:

- Use the standard System.Nullable<T> type on the .Net side
- As much type-safety as possible on the ActionScript side (i.e., few/no
*-typed variables)
- Since our entities often have many properties and inheritance, we'd
like to stay away from custom IExternalizable implementations for them
- A sentinel value (like the <nullable> config section) won't work
because we don't know what (if any) value won't be used

With these ideas in mind, we've come up with a solution that would work
if a small change was made in FluorineFx.Util.Convert. I'll walk you
through the idea and what would need to change.

In ActionScript, we make small classes that act like .Net's Nullable<T>
and implement IExternalizable in a sensible way. The setter on the Value
property is untyped to allow it to be assigned to null, the actual data
type, or the nullable type. For instance, here's NullableBoolean:

        import flash.errors.IllegalOperationError;
        import flash.utils.IDataInput;
        import flash.utils.IDataOutput;
        import flash.utils.IExternalizable;
       
        [RemoteClass(alias="ServiceLibrary.NullableBoolean")]
        public class NullableBoolean implements IExternalizable
        {
                private var _Value:Boolean;
                public function get Value():Boolean {
                        if(!HasValue)
                                throw new IllegalOperationError("The
object has no value");
                       
                        return _Value;
                }
               
                public function set Value(val:*):void {
                        if(val == null)
                                _HasValue = false;
                        else if(val is Boolean) {
                                _Value = val;
                                _HasValue = true;
                        }
                        else if(val is NullableBoolean) {
                                var ni:NullableBoolean =
NullableBoolean(val);
                                if(ni.HasValue) _Value = ni.Value;
                                else _HasValue = false;
                        }
                        else
                                throw new TypeError("The given value is
not of an appropriate type");
                }
               
                private var _HasValue:Boolean;
                public function get HasValue():Boolean { return
_HasValue; }
               
                public function NullableBoolean(val:* = null)
                {
                        Value = val;
                }
               
                public function readExternal(input:IDataInput):void {
                        Value = input.readObject() as Boolean;
                }
               
                public function writeExternal(output:IDataOutput):void {
                        output.writeObject(HasValue ? Value : null);
                }
        }

Now, on classes that have nullable members, we use this type like so:

        [RemoteClass(alias="ServiceLibrary.ObjectWithNullableBool")]
        public class ObjectWithNullableBool
        {
                private var _PossibleBool:NullableBoolean = new
NullableBoolean();
                public function get PossibleBool():NullableBoolean {
return _PossibleBool; }
                public function set PossibleBool(val:*):void {
_PossibleBool.Value = val; }
        }
       
Since we just pass through the assignment, we can assign to PossibleBool
with null, a Boolean, or another NullableBoolean.

Now, on the .Net side, there's a counterpart to the NullableBoolean type
that's really just a proxy for Nullable<Boolean> with an IExternalizable
and IConvertible implementation:

    [Serializable]
    internal struct NullableBoolean: IExternalizable, IConvertible
    {
        public Nullable<Boolean> Value { get; private set; }

        void IExternalizable.ReadExternal(IDataInput input)
        {
            Value = (Nullable<Boolean>)input.ReadObject();
        }

        void IExternalizable.WriteExternal(IDataOutput output)
        {
            if (Value.HasValue) output.WriteObject(Value);
            output.WriteObject(null);
        }

          // Unimplemented IConvertible members omitted
               
          bool IConvertible.ToBoolean(IFormatProvider provider)
        {
            return (bool)Value;
        }
    }

This is where we hit the problem. Fluorine eventually tries to convert
our NullableBoolean type to a Nullable<Boolean> using
FluorineFx.Convert.ToNullableBoolean(object). This function (and its
versions for different types) has a very specific idea about what is
null:

        if (value == null || value is DBNull) return null;
       
Since the value variable is of type object at this time, the == operator
is the one from System.Object, which we can't override. So, we have no
way to tell Fluorine when our NullableBoolean type is actually holding a
null. To remedy this, I suggest that the check above (and in all the
Convert.ToNullableType(object) functions) be modified to understand the
System.Data.SqlTypes.INullable interface. That way we could just add the
code below to NullableBoolean and have a pretty seamless nullable
implementation:

        [Transient]
        bool INullable.IsNull
        {
        get { return !Value.HasValue; }
        }

Sound reasonable? The only drawback I can see is that INullable doesn't
have any information about the underlying type, so the modified Convert
functions would convert any INullable type reporting IsNull to any other
nullable type. That's an issue with DBNull as well, really.

Or, perhaps this is going way overboard and there's an easier solution.
Thoughts?

--Tim Clem
RIA Developer, Stream57

_______________________________________________
fluorine mailing list
fluorine@...
http://fluorine.thesilentgroup.com/mailman/listinfo/fluorine_fluorine.thesilentgroup.com