Welcome to Atalasoft Community Sign in | Help

Using a Proxy Class to Fix F# Protected Access Limitation

In this previous entry, I presented a workaround to F# not having support for protected members.  Upon thinking on it further, I decided that this could nearly be fixed with an adapter class.  The adapter class would be implemented in C# and would override all protected members and call optional/non-optional delegates to perform the actual work.

If you wanted to implement an ImageCommand from dotImage in F#, you would start with a C# class like this:

using System;
using System.Drawing;
using Atalasoft.Imaging;
using Atalasoft.Imaging.ImageProcessing;
using System.Runtime.Serialization;
namespace DelegatedImageCommander
{
    public delegate AtalaImage PerformActualCommandDelegate(AtalaImage source, AtalaImage dest, Rectangle area, ref ImageResults results);
    public abstract class DelegatedImageCommand : ImageCommand
    {
        Func<AtalaImage, AtalaImage> _constructChangedSourceImage, _constructFinalImage;
        Func<ImageResults> _constructImageResults;
        Func<AtalaImage, PixelFormat, AtalaImage> _getChangedPixelFormat;
        PerformActualCommandDelegate _performActualCommand;
        Func<AtalaImage, PixelFormat, PixelFormat[], PixelFormat> _selectBestAlternatePixelFormat, _selectPreferredPixelFormat;
        Action<AtalaImage> _verifyProperties;
        protected DelegatedImageCommand(PerformActualCommandDelegate performActualCommand,
            Action<AtalaImage> verifyProperties,
            Func<AtalaImage, AtalaImage> constructChangedSourceImage,
            Func<AtalaImage, AtalaImage> constructFinalImage,
            Func<ImageResults> constructImageResults,
            Func<AtalaImage, PixelFormat, AtalaImage> getChangedPixelFormat,
            Func<AtalaImage, PixelFormat, PixelFormat[], PixelFormat> selectBestAlternatePixelFormat,
            Func<AtalaImage, PixelFormat, PixelFormat[], PixelFormat> selectPreferredPixelFormat)
        {
            if (performActualCommand == null)
                throw new ArgumentNullException("performActualCommand");
            _performActualCommand = performActualCommand;
            _verifyProperties = verifyProperties;
            _constructChangedSourceImage = constructChangedSourceImage;
            _constructFinalImage = constructFinalImage;
            _constructImageResults = constructImageResults;
            _getChangedPixelFormat = getChangedPixelFormat;
            _selectBestAlternatePixelFormat = selectBestAlternatePixelFormat;
            _selectPreferredPixelFormat = selectPreferredPixelFormat;
        }
        protected DelegatedImageCommand(PerformActualCommandDelegate performActualCommand, Action<AtalaImage> verifyProperties)
            : this(performActualCommand, verifyProperties, null, null, null, null, null, null)
        {
        }
        protected override AtalaImage ConstructChangedSourceImage(AtalaImage image)
        {
            if (_constructChangedSourceImage != null)
                return _constructChangedSourceImage(image);
            return base.ConstructChangedSourceImage(image);
        }
        protected override AtalaImage ConstructFinalImage(AtalaImage image)
        {
            if (_constructFinalImage != null)
                return _constructFinalImage(image);
            return base.ConstructFinalImage(image);
        }
        protected override ImageResults ConstructImageResults()
        {
            if (_constructImageResults != null)
                return _constructImageResults();
            return base.ConstructImageResults();
        }
        protected override AtalaImage GetChangedPixelFormat(AtalaImage sourceImage, PixelFormat newFormat)
        {
            if (_getChangedPixelFormat != null)
                return _getChangedPixelFormat(sourceImage, newFormat);
            return base.GetChangedPixelFormat(sourceImage, newFormat);
        }
        protected override AtalaImage PerformActualCommand(AtalaImage source, AtalaImage dest, Rectangle imageArea, ref ImageResults results)
        {
            return _performActualCommand(source, dest, imageArea, ref results);
        }
        protected override PixelFormat SelectBestAlternatePixelFormat(AtalaImage sourceImage, PixelFormat sourceFormat, PixelFormat[] formats)
        {
            if (_selectBestAlternatePixelFormat != null)
                return _selectBestAlternatePixelFormat(sourceImage, sourceFormat, formats);
            return base.SelectBestAlternatePixelFormat(sourceImage, sourceFormat, formats);
        }
        protected override PixelFormat SelectPreferredPixelFormat(AtalaImage sourceImage, PixelFormat sourceFormat, PixelFormat[] formats)
        {
            if (_selectPreferredPixelFormat != null)
                return _selectPreferredPixelFormat(sourceImage, sourceFormat, formats);
            return base.SelectPreferredPixelFormat(sourceImage, sourceFormat, formats);
        }
        protected override void VerifyProperties(AtalaImage image)
        {
            if (_verifyProperties != null)
                _verifyProperties(image);
        }
    }
}

Then in F#, instead of inheriting from ImageCommand, you would inherit from DelegatedImageCommand and provide a set of delegates to perform the actual work.  In ImageCommand, you can get away with default behavior for everything except PerformActualCommand and you’re good to go.  I provided two constructors: one with all the delegates and one with the most common case.  Now an F# class that inherits from DelegatedImageCommand will present the correct interface to the world.  Almost.

I say almost because ImageCommand is serializable, and as such you are supposed to create a constructor that is protected which takes a SerializationInfo object and a StreamingContext object.  F# doesn’t let you make protected constructors, so this approach won’t work properly.  It turns out that the protected constructor can be public – it is only suggested that it is protected.

However, if you want to do this properly, you would need to define a shell class in C# that has the correct public interface and data/property model and then do the actual work in F#.

Published Tuesday, August 10, 2010 11:11 AM by Steve Hawley

Comments

# Atalasoft Blog on: Atalasoft: Using a Proxy Class to Fix F# Protected Access Limitation | Social Networking &amp; Digital Collaboration news, tips, guides...

Anonymous comments are disabled