Любой способ использовать интерфейсы как "псевдонимы типа" в C#? - PullRequest
1 голос
/ 03 мая 2020

По сути, я хочу использовать свои собственные типы вместо примитивов, таких как int / double, но все же обойти эти примитивные значения. Что-то вроде:

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:(

Ответы [ 4 ]

2 голосов
/ 04 мая 2020

По предложению ispiro я нашел что-то, что должно охватывать все.

Поэтому я объявляю свои интерфейсы независимыми от базового представления, например,

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

Они имеют полную общность интерфейса, такую ​​как множественное наследование. Затем представление данных выполняется с помощью обобщенных c классов с неявным преобразованием:

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 };
        }

Функции, которые принимают IPostNumber с int, выполняются так:

    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

Теперь я всегда могу объявить CString<IPostNumber>, если некоторые номера постов представлены в виде строки. Или функция может принять сам интерфейс IPostNumber, если я сделаю какой-то его класс. Теперь одна небольшая проблема заключается в том, что если я хочу передать CInt<IPostNumberFrom> в TestPostNumber, метод должен быть generi c с T : IPostNumber, например:

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

и затем generi c тип не будет обнаружен при использовании неявного преобразования (должен быть приведен). Но посмотрим, будет ли это важно.

Также для дальнейшего рассмотрения: у меня будет class CJSON<T> : CString<T>. Из того, что я вижу, это работает, хотя argubly CJSON может иметь и другие представления, например byte[] в некотором контексте. (Но это далеко уходит). Так что просто нужно серьезно подумать о представлении и интерфейсах для моих концепций домена.

2 голосов
/ 04 мая 2020

Я думаю, что вы, возможно, ищете неявные операторы, но, к сожалению, я считаю, что они не поддерживаются для интерфейсов в спецификации языка C#. Вы можете сделать это с подклассами, если это будет необходимо. Вот пример:

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;
   }
}

Чтобы назначить с помощью неявного оператора, вы можете сделать это:

MyInt n = 3;
int x = n;

См .: неявный оператор с использованием интерфейсов

1 голос
/ 03 мая 2020

Это то, что вы ищете?

public class IInt
{
    public int TheInt;

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

, а затем либо используйте:

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

, либо определите MyFunction для int, а затем используйте:

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

Еще одна идея:

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

    public IInt(T theInt)
    {
        TheInt = theInt;
    }
}
0 голосов
/ 03 мая 2020

Вы можете создать метод расширения, но этот метод должен быть явно вызван.

...