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*/ }
//...
}