C # общий метод с ограничением типа интерфейса

Предположим, что у меня есть:

  • A generic method Get
  • A few interfaces IEntity, IValue
  • A few classes that respectively implements those interfaces ex: Entity -> IEntity, Value -> IValue etc.

=> Is there a way for the Get method to allow the interfaces only as generic Types?

Get(42); //Allowed
Get(42);  //Compiler error

Мое текущее решение выглядит так:

  • A generic method Get with a Type constraint where T: IPersistable (to prevent most of the types to be passed as a parameter)
  • The interfaces implement IPersistable

Функция активно проверяет тип:

public T Get(long id) where T : IPersistable
{
   if (typeof (T) == typeof (IEntity))
      return (T) EntityDao.Get(id);
   if (typeof (T) == typeof (IValue))
      return (T) ValueDao.Get(id);

   //...

   throw new TechnicalException("Type not supported");
}

=> The problem are:

  1. It is not clean... I could live with that since there are only very few types to check from
  2. The signature does not match what the function really does. It allows an IPersistable in, but not really <- that really bugs me :(

Edit: I'm considering such constraints to avoid surpopulation of my class.

У меня есть что-то вроде 8 или 9 общих методов в этом классе, которые все так хорошо работают. Интуитивно понятный способ работы был бы таким, как @DanielHilgarth предложил использовать только один метод для каждого типа. В настоящее время методы могут быть вызваны только с 4 или 5 типами. Но все же, это будет означать 32-40 методов в этом классе.

Если возможно, я бы хотел этого избежать.

Edit2: the need for preventing "real" classes to be called comes from a covariance/contravariance problem. The EntityDao and ValueDao Get methods return IEntity and IValue objects. What works fine when I query for a single object fails when I call for a collection in a GetAll methods since I cannot cast an IEnumerable in an IEnumerable.

Я просто заметил этот ответ от @JonSkeets о литье списков. Это может быть обходным путем ...

3
nl ja de
Не могли бы вы объяснить, зачем вам такое ограничение?
добавлено автор Dennis, источник
Это объяснение, почему вы хотите сделать общий метод isntead из числа заводских методов. Было бы полезно, если вы объясните, почему этот метод должен быть ограничен только типами интерфейсов.
добавлено автор Dennis, источник
Конечно, я только что редактировал вопрос.
добавлено автор Tim Bourguignon, источник
Обновлено снова;)
добавлено автор Tim Bourguignon, источник

2 ответы

Вы должны просто создавать специальные методы. Пример кода с помощью if показывает, что ваш текущий метод не делает ничего. Он много.

Просто пойдите с:

GetEntity(42);
GetValue(13);

public IEntity GetEntity(long id)
{
    return EntityDao.Get(id);
}

public IValue GetValue(long id)
{
    return ValueDao.Get(id);
}

Это много чище на всех слоях:

  1. GetEntity vs. Get
  2. You clearly communicate what is possible. You don't have any runtime exceptions.
  3. Your get methods don't need any type switching.

If this will result in too many similar methods on your service, it is time to break out new classes, e.g. one for Entity and one for Value. Then you could give your service properties that return the new classes. That's the same I am doing when implementing my query objects. It could then look like this: service.Values.Get(13) and service.Entities.Get(42)

5
добавлено
@TimBourguignon: В этом случае вы должны выловить некоторые новые классы.
добавлено автор Daniel Hilgarth, источник
Вы можете предоставить свои свойства сервиса, которые возвращают новые классы. То же самое я делаю при реализации моих объектов запроса . Это может выглядеть так: service.Values.Get (13) и service.Entities.Get (42) .
добавлено автор Daniel Hilgarth, источник
Я мог бы тогда бояться за «перенаселенность» методов в моем классе. У меня есть что-то вроде 8 или 9 общих методов, все работают аналогичным образом в этом классе. Если бы я взорвал свои общие методы, я бы приземлился с чем-то вроде 40 методов. Если возможно, я бы хотел этого избежать.
добавлено автор Tim Bourguignon, источник
Это действительно возможность ... которую я пытался избежать, поскольку единственная точка входа в эту службу действительно удобна (все упомянутые методы - это геттеры, поэтому в любом случае имеет смысл объединить их все вместе) ...
добавлено автор Tim Bourguignon, источник
Мне нужно об этом немного поразмыслить, но это хорошая идея, она выглядит сексуально: D
добавлено автор Tim Bourguignon, источник

Это может быть альтернатива:

abstract class Persistable
{
    protected static Func mapFunction;
    public static T Get(long id)
    {
        return mapFunction(id);
    }
}

class Entity : Persistable
{
    public static Entity()
    {
        Persistable.mapFunction = input => EntityDao.Get(input);
    }
}
class Value : Persistable
{
    public static Value()
    {
        Persistable.mapFunction = input => ValueDao.Get(input);
    }
}

Ваш метод Get будет выглядеть примерно так:

public T Get(long id)//maybe you restrict the T to something
{
    Persistable.Get(id);
}
1
добавлено
Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot