1

Basically I want to use my own types instead of primitives like int/double, but still pass around these primitive values. Something like:

interface IInt {} // My interface to represent int. If I could fake so "int" implements this, all would work.

interface IPostNumber : IInt {} // Post number is an int. But int is not type safe enough for me.

void MyFunction(IPostNumber postNumber); // My function that should accept int/IPostNumber.

MyFunction(42); // This could also work with implicit conversion, but not allowed for interfaces:(
user2154768
  • 890
  • 3
  • 8
  • 16
  • 2
    I think you're looking for what's called a "using alias directive". – Patrick Roberts May 03 '20 at 19:33
  • using IPostNumber = System.Int32; using IAccountNumber = System.Int32; Can be used, but the type system will not ensure that IAccountNumber is not passed as IPostNumber. And I also want to use the interface for other interface stuff. – user2154768 May 03 '20 at 19:54
  • 2
    What exact problem do you need to solve? This seems like massive overthinking. – CSharpie May 03 '20 at 20:40
  • 1
    Question: why are you going to use custom interfaces instead of value types like int, double, etc? – Marlonchosky May 03 '20 at 21:56

4 Answers4

3

From ispiro's suggestion I found something that should cover everything.

So I declare my interfaces independent of underlying representation, e.g.

public interface IPostNumber{}
public interface IPostNumberFrom : IPostNumber{}
public interface IPostNumberTo : IPostNumber{}

These have full interface generality such as multiple inheritance. Then the data representation is done with generic classes with implicit conversion:

public class CInt<T>
        {
            public int value;
            public static implicit operator int(CInt<T> d) => d.value;
            public static implicit operator CInt<T>(int b) => new CInt<T>() { value = b };
        }

Functions that accepts IPostNumber with int, is done as such:

    private int TestPostNumberInt(CInt<IPostNumber> i) => i;
    private int TestPostNumberFrom(CInt<IPostNumberFrom> i) => i;

        CInt<IPostNumber> a = 4; // Works
        Assert.Equal(1, TestPostNumberInt(1));  // Works
        Assert.Equal(1, TestPostNumberFrom(a)); // Don't compile with IPostNumber into IPostNumberFrom

Now I can always declare CString<IPostNumber>, if some post numbers are represented with string. Or a function could accept the IPostNumber interface itself, if I make some class of it. Now one little issue is that if I want to pass CInt<IPostNumberFrom> to TestPostNumber, the method must be generic with T : IPostNumber, like this:

    private int TestPostNumberInt<T>(CInt<T> i) where T : IPostNumber => i;
    private int TestPostNumberIntFrom<T>(CInt<T> i) where T : IPostNumberFrom => i;

and then the generic type will not be detected while using implicit conversion (must cast). But we'll see if this is a big deal.

Also for later consideration: I will have class CJSON<T> : CString<T>. From what I see it works, though argubly CJSON could have different representations as well, like byte[] in some context. (But this is taking things far). So just need to think hard about representation vs. interfaces for my domain concepts.

user2154768
  • 890
  • 3
  • 8
  • 16
2

I think what you might be looking for are implicit operators, but unfortunately I believe they're not supported for interfaces in the C# language specification. You can do this with subclasses if need be. Here's an example:

public class MyInt
{
   int SomeValue;
   public TestInt(int i)
   {
       SomeValue = i;
   }

   public static implicit operator MyInt(int i)
   {
       return new MyInt(i);
   } 

   public static implicit operator int(MyInt myInt)
   {
       return myInt.SomeValue;
   }
}

To assign using an implicit operator, you can do this:

MyInt n = 3;
int x = n;

See: implicit operator using interfaces

Alan Samet
  • 1,118
  • 2
  • 15
  • 18
1

Is it this you're looking for?

public class IInt
{
    public int TheInt;

    public IInt(int theInt)
    {
        TheInt = theInt;
    }
}

and then either use:

IInt i = new IInt(42);
MyFunction(i);

or define MyFunction for int and then use:

IInt i = new IInt(42);
MyFunction(i.TheInt);

Just one more idea:

public class IInt<T> where T : struct
{
    public T TheInt;

    public IInt(T theInt)
    {
        TheInt = theInt;
    }
}
ispiro
  • 26,556
  • 38
  • 136
  • 291
  • Main issue with using class or struct, is that I want to also inherit it by an interface afterwards. Also I was hoping to avoid the performance cost of wrapping the int with struct or class. – user2154768 May 03 '20 at 20:14
  • @user2154768 You can define an interface and make `IInt` implement it maybe? Perhaps explain a little more why you need the interface? What will you do with it? – ispiro May 03 '20 at 20:17
  • As for performance, as far as I know, if you're not going to use it in some tight loop many many times, it won't really matter. But of course that depends on your exact scenario. – ispiro May 03 '20 at 20:19
  • I will setup inhertiances such as IPostNumberFrom : IPostNumber : IInt, e.g. many layers, and multiple inheritance. I could also use a separate empty IPostNumber interface, with IPostNumberInt : IPostNumber,IInt and IPostNumberString : IPostNumber,IString. I will use reflection to read my interfaces and generate some graphs that I will use to find function paths for converting from one type to another, by looking up all my functions with reflection. Then maybe some code generation to generate efficient implementation of the paths. Plan B is function parameter/return attributes... – user2154768 May 03 '20 at 20:26
  • @user2154768 OK. So I'm out of ideas :) . But I did post one more idea in my answer - making the class generic. Good luck! – ispiro May 03 '20 at 20:30
  • The generic example has some potential, since I only need one class for each representation, and can separate interfaces completely from it. I am looking for potential issues with implicit conversion, but it may work out. – user2154768 May 03 '20 at 21:53
0

You can create an extension method but that method should be explicitly called.

Tony Troeff
  • 338
  • 4
  • 19