Массив arg Expression<Func <object >> как часть свободного интерфейса

Рассмотрим такой интерфейс:

new Provider().For(myClass).ExcludeProperties("Height", "Width");

public IEditableStateProvider For(object target) {...}
public IEditableStateProvider ExcludePropertyNames(params string[] propertyNames) {...}

Я хочу заменить params string[] propertyNames спорить с params Expression<Func<object>>[] propertyNames так что вместо этого у меня будет следующее.

new Provider().For(myClass).ExcludeProperties(()=>Height, ()=>Width);

Я видел код, похожий на этот, поэтому я думаю, что он должен работать, но я пока не понимаю. Как я могу заставить это работать?

РЕДАКТИРОВАТЬ - делать это без Generics

Вот код из проекта с открытым исходным кодом, который я смотрел, где вывод типов работает без каких-либо обобщений. Я хотел сделать то же самое, но я не вижу, откуда исходит вывод типа (хотя я вижу, что это работает!)

// USAGE (here this is being called from code-behind of a WPF window

private void TrackSelectedTab() {
    Services.Tracker.Configure(tabControl)
        .AddProperties(() => tabControl.SelectedIndex);
    Services.Tracker.ApplyState(tabControl);
}

private void TrackMainWindow() {
    Services.Tracker.Configure(this)
        .AddProperties(
            () => Height,
            () => Width,
            () => Left,
            () => Top,
            () => WindowState)
        .SetKey("MainWindow")
        .SetMode(PersistModes.Automatic);

    Services.Tracker.ApplyState(this);
}

// Collab classes

public class SettingsTracker
{   
    public TrackingConfiguration Configure(object target) {
        ...
        return config;
    }
}

public class TrackingConfiguration
{
    public TrackingConfiguration AddProperties(params Expression<Func<object>>[] properties) {
        ...
        return this;
    }
}

static class Services
{
    public static readonly SettingsTracker Tracker = new SettingsTracker(ObjectStore);
}

1 ответ

Решение

Вы должны создать общий Provider класс в дополнение к неуниверсальному, так что вы можете воспользоваться преимуществами вывода типов и безопасности типов:

Интерфейс:

interface IEditableStateProvider<T>
{
     IEditableStateProvider<T> For(T target);
     IEditableStateProvider<T> ExcludePropertyNames(params Expression<Func<T, object>>[] excludedProperties);
} 

Фиктивная реализация:

class Provider<T> : IEditableStateProvider<T>
{
    public IEditableStateProvider<T> For(T target) 
    { 
        // dummy
        return this;
    }
    public IEditableStateProvider<T> ExcludePropertyNames(params Expression<Func<T, object>>[] excludedProperties) 
    {
        // dummy
        return this;
    }
}
class Provider
{
    // generic factory method to make use of type inference
    public static IEditableStateProvider<T> For<T>(T obj)
    {
        return new Provider<T>().For(obj);
    }
}

Использование:

var myClass = new List<object>(); // or whatever
Provider.For(myClass).ExcludePropertyNames(x => x.Count);

Тип T теперь выводится, когда вы звоните .For(myClass)и теперь вы можете получить доступ к свойствам типа T безопасным способом через лямбду при вызове ExcludePropertyNames,


Если вы действительно хотите неуниверсальную версию:

interface IEditableStateProvider
{
     IEditableStateProvider For(object target);
     IEditableStateProvider ExcludePropertyNames(params Expression<Func<object>>[] excludedProperties);
} 
class Provider : IEditableStateProvider
{
    public IEditableStateProvider For(object target) 
    { 
        // dummy
        return this;
    }
    public IEditableStateProvider ExcludePropertyNames(params Expression<Func<object>>[] excludedProperties) 
    {
        // dummy
        return this;
    }
}

var myClass = new List<object>();
new Provider().For(myClass).ExcludePropertyNames(() => myClass.Count);

но, пожалуйста, обратите внимание на мой комментарий ниже.

Другие вопросы по тегам