Home > Back-end >  Interface with internal implementation only
Interface with internal implementation only

Time:06-20

I am currently writing a library that encodes and decodes images in qoi format. This image format uses rgb and rgba pixel formats.

I made the IPixel interface and two implementations RgbPixel and RgbaPixel. IPixel is not intended to be used as an extension point (because there are only rgb and rgba), I made this interface to use generic classes so as not to write the same thing twice.

RgbPixel and RgbaPixel are structs with the [StructLayout] attribute. Some of my generic classes use unsafe code to unsafely cast these structs and other unmanaged types to each other to improve performance, so if user/another implementation of IPixel gets to them, it will most likely cause an exception.

To avoid all these problems, I would like to allow only internal implementation of the IPixel interface. But I don't know how to do it in C#.

If IPixel were an abstract class, I could prevent implementation from other assemblies by having only internal constructors. But I can only use interfaces because I need RgbPixel and RgbaPixel to be structs.

.netstandart 2.0, c# 10.0

CodePudding user response:

I would make IPixel internal and have public shim methods that accept RgbPixel and RgbaPixel which then call the common method that accepts IPixel (or a base class).

CodePudding user response:

I made all the class constructors internal and added two static methods Load and New<T>, the first one takes an encoded data and by decoding the header it creates either QImage<RgbPixel> or QImage<RgbaPixel>, the second method merely checks the type parameter passed by the user, as suggested by Kirk Woll in the comments.

Looks something like this:


public class QImage : IImage {
internal QImage(/*args*/) { /*...*/ }

public static QImage Load(byte[] bytes) {
    //...

    var qr = new QoiBytesReader(bytes);
    var qHeader = Decoder.DecodeHeader(ref qr);

    //ugly :|
    return qHeader.Channels == Channels.Rgb 
         ? new QImage<RgbPixel>(Decoder.DecodePixels<RgbPixel>(ref qr, in qHeader), qHeader) 
         : new QImage<RgbaPixel>(Decoder.DecodePixels<RgbaPixel>(ref qr, in qHeader), qHeader);
    }

//...
}

public class QImage<T> : QImage where T : unmanaged, IPixel {
    internal QImage(/*args*/) : base(/*params*/) { /*...*/ }

public static QImage<T> New(byte[] pixels, QHeader qHeader) {
    if (typeof(T) == typeof(RgbPixel) || typeof(T) == typeof(RgbaPixel)) 
        return new QImage<T>(pixels, qHeader);
        
    throw new NotSupportedException($"Specified type <{typeof(T).Name}> is not supported. Use <{nameof(RgbPixel)}> or <{nameof(RgbaPixel)}> instead.");
    }
//...
}

internal static unsafe class Decoder {
internal static byte[] DecodePixels<T>(ref QoiBytesReader qr, in QHeader metadata) 
    where T : unmanaged, IPixel { /*decode*/ }

//...
}

  • Related