-1

I'm using ImageProcessor in C# and I can't come up with a way to make a generic method out of its methods as to avoid writing the same code more than once.

Say, I have two methods, crop and resize.

 public Image Resize(Image image, int width, int height)
    {
        // Create a Image to be returned after being scaled, call it resizedImage:
        Image resizedImage;
        // Create a Size for the image to be scaled to, call it size:
        var size = new Size(width, height);
        // Create an ImageFactory to process the image, call it imageFactory:
        using (var imageFactory = new ImageFactory(preserveExifData: true))
        {
            // Create a MemoryStream to temporarily store the processed image, call it imageStream:
            using (var imageStream = new MemoryStream())
            {
                // Scale the image using the ImageProcessor library
                // Load the image to be resized, resize it and save it to the MemoryStream:
                imageFactory.Load(image)
                            .Resize(size)
                            .Save(imageStream);
                // Assign the processed image to the previously declared resizedImage:
                resizedImage = Image.FromStream(imageStream);
            }
        }

        // Return the resized image:
        return resizedImage;
    }

public Image Crop(Image image, float left, float top, float right, float bottom, bool isModePercentage = true)
    {
        // Create a Image to be returned after being cropped, call it croppedImage:
        Image croppedImage;
        // Create a CropMode to specify what cropping method to use (Percentage or Pixels), call it cropMode
        // If isModePercentage = true we use CropMode.Percentage, otherwise use CropMode.Pixels:
        var cropMode = isModePercentage ? CropMode.Percentage : CropMode.Pixels;
        // Create a CropLayer to specify how and how much the image should be cropped, call it cropLayer:
        var cropLayer = new CropLayer(left, top, right, bottom, cropMode);
        // Create an ImageFactory to process the image, call it imageFactory:
        using (ImageFactory imageFactory = new ImageFactory(preserveExifData: true))
        {
            // Create a MemoryStream to temporarily store the processed image, call it imageStream:
            using (MemoryStream imageStream = new MemoryStream())
            {
                // Crop the image using the ImageProcessor library
                // Load the image to be cropped, crop it and save it to the MemoryStream:
                imageFactory.Load(image)
                            .Crop(cropLayer)
                            .Save(imageStream);
                // Assign the processed image to the previously declared croppedImage:
                croppedImage = Image.FromStream(imageStream);
            }
        }
        // Return the cropped image:
        return croppedImage;
    }

How can I avoid having to instantiate imagefactory and memory stream multiple times, reducing the size of my methods and avoid to write the same code twice?

  • 3
    I would say a more suitable place to ask this question is at [codereview.stackexchange.com](https://codereview.stackexchange.com/). If you decided to do so, please make sure to follow the guidelines in the [Help Center](https://codereview.stackexchange.com/help). – 41686d6564 stands w. Palestine Apr 10 '20 at 23:53
  • 1
    Why would you make it less readable while refactoring it. I think they are both separated functionality which share some common implementation. – Jeroen van Langen Apr 11 '20 at 00:05

2 Answers2

1

Such code is often can be refactored using template method design pattern. In simple case like this where the only one part varies it may be simplified to just pass that part as parameter rather than going full inheritance approach:

public Image CloneViaStream(Image image, 
       Func<TypeFromImageFactoryLoad, TypeForSaveCall> populateStream)
{
    using (var imageFactory = new ImageFactory(preserveExifData: true))
    {
        using (var imageStream = new MemoryStream())
        {
            TypeFromImageFactoryLoad loadResult = imageFactory.Load(image);
            TypeForSaveCall processResult = process(loadResult);
            processResult.Save(imageStream);

            imageStream.Location = 0;
            return Image.FromStream(imageStream);
        }
    }
}

and call this method with:

var resultResize = CloneViaStream(image, 
     funcParam1 => funcParam1.Resize(new Size(width, height)));

var cropLayer = ...
var resultCrop = CloneViaStream(image, 
     loadResult => loadResult.Crop(cropLayer)));

You can also return Func<Image, Image> if you want to apply the same transformation to multiple images.

Note that whether this is good idea or not is purely personal preference. To some extent since you original code was wrong indeed it is better to have such code in one place as you don't need to update multiple methods with imageStream.Location =0;... But on other hand it may be harder to read.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Sorry but I still do not understand how I could use this. What is "process"? How can I use "loadResult" if its a local variable on "CloneViaStream"? – Filipe Ribeiro Apr 11 '20 at 17:01
  • @FilipeRibeiro sorry, I don't know how to explain calling delegates/ `Func<...>`. I made tiny edit (renamed one of the parameters) to show that there is no relation between local variables inside `CloneViaStream` and names of parameters of `Func<...>` you pass to it. You may want to search for something like "C# explain func" and "C# explain delegate" (i.e. https://stackoverflow.com/questions/878650/explanation-of-func) to see if one of the explanations clicks. – Alexei Levenkov Apr 11 '20 at 20:13
-1

you can try Func<> to generate dynamic method, then you can call like var result = funcImageFactory(img,w,h); in any method.(as you realize in Func< Image, int, int, Image> is your return type and others are parameters)

public static Func<Image, int, int, Image> funcImageFactory = (image, width, height) =>
{
    using (var imageFactory = new ImageFactory(preserveExifData: true))
    {
        // Create a MemoryStream to temporarily store the processed image, call it imageStream:
        using (var imageStream = new MemoryStream())
        {
            // Scale the image using the ImageProcessor library
            // Load the image to be resized, resize it and save it to the MemoryStream:
            imageFactory.Load(image)
                        .Resize(size)
                        .Save(imageStream);
            // Assign the processed image to the previously declared resizedImage:
           var resizedImage = Image.FromStream(imageStream);
           return resizedImage;
        }
    }
};