Если я правильно понимаю, вы, по сути, хотите, чтобы компилятор выводил один из универсальных типов из другого. Вы можете немного приблизиться к этому, используя метод статического универсального построения, но вам придется пойти на компромисс и заставить Watcher‹T, TKey› реализовать интерфейс только с одним параметром универсального типа. Ниже я попытаюсь проиллюстрировать, а вы сами решите, стоит ли идти на компромисс.
Вот ваш существующий класс Watcher..
public class Watcher<T, TKey> : IWatcher<TKey> where T : IWatchable<TKey>
{
public Watcher(IWatchable<TKey> target) { }
}
и вот интерфейс, который нужно будет реализовать:
public interface IWatcher<TKey> { }
Теперь нам понадобится необобщенный статический класс Watcher, который будет содержать универсальный метод, для которого потребуется только один параметр типа:
public static class Watcher
{
public static IWatcher<TKey> For<TKey>(IWatchable<TKey> target)
{
return new Watcher<IWatchable<TKey>, TKey>(target);
}
}
Обратите внимание, что сигнатура типа имеет IWatcher‹TKey› в качестве возвращаемого типа, хотя она создает Watcher‹IWatchable‹TKey›, TKey›. Этот трюк позволяет нам указать только один параметр типа.
Следующий трюк — полагаться на вывод типа C#, чтобы нам не нужно было указывать тип «TKey» при вызове метода «For». Если мы взяли класс:
public class BeingWatched : IWatchable<int>
{
public BeingWatched(int key)
{
Key = key;
}
public int Key { get; }
}
то мы можем получить наблюдателя для этого экземпляра с помощью следующего кода:
var watcher = Watcher.For(new BeingWatched(123));
Вывод типа избавляет нас от необходимости явно писать
var watcher = Watcher.For<int>(new BeingWatched(123));
Это работает до тех пор, пока нет двусмысленности. Если у вас есть класс
public class AlsoBeingWatched : IWatchable<int>, IWatchable<Guid>
{
private readonly int _numberKey;
private readonly Guid _guidKey;
public AlsoBeingWatched(int numberKey, Guid guidKey)
{
_numberKey = numberKey;
_guidKey = guidKey;
}
int IWatchable<int>.Key { get { return _numberKey; } }
Guid IWatchable<Guid>.Key { get { return _guidKey; } }
}
тогда
var watcher = Watcher.For(new AlsoBeingWatched(123, Guid.NewGuid()));
не скомпилируется, вы получите ошибку
The type arguments for method 'Watcher.For<TKey>(IWatchable<TKey>)' cannot be inferred from the usage.
Вам нужно будет явно указать либо
var watcher = Watcher.For<int>(new AlsoBeingWatched(123, Guid.NewGuid()));
or
var watcher = Watcher.For<Guid>(new AlsoBeingWatched(123, Guid.NewGuid()));
Этот подход может быть не совсем тем, о чем вы просили (или, возможно, на что вы надеялись), но я думаю, что это лучший способ избежать явного указания типов во многих распространенных случаях.
person
Dan Roberts
schedule
31.08.2016
BeingWatched
реализовалIWatchable<int>
иIWatchable<Guid>
, тоWatcher
не знал бы, какую реализацию использовать вBeingWatched
? Вот почему он должен быть явным. - person iswinky   schedule 31.08.2016where
используется только для проверки ограничений, а не для разрешения типов... Однако нужно ли вам вообще знатьT
вWatcher
? Может быть достаточно зарегистрировать наблюдателя какpublic class Watcher<TKey>
и работать сIWatchable
вместо конкретного типа? - person grek40   schedule 31.08.2016