Неявное преобразование без присваивания? - PullRequest
4 голосов
/ 13 сентября 2010

Сохраненный вопрос - см. Редактировать внизу
Я работаю над небольшой функциональной библиотекой, в основном, чтобы обеспечить некоторую читаемость, скрывая основные цикломатические сложности. Поставщик называется Select<T> (с вспомогательной фабрикой, называемой Select), и использование аналогично

public Guid? GetPropertyId(...)
{
    return Select
        .Either(TryToGetTheId(...))
        .Or(TrySomethingElseToGetTheId(...))
        .Or(IGuessWeCanTryThisTooIfWeReallyHaveTo(...))
        //etc.
        ;
}

и библиотека позаботится о коротком замыкании и т. Д. Я также добавил неявное преобразование из Select<T> в T, чтобы я мог написать

public Guid GetPropertyId(...)
{
    ServiceResult result = Select
        .Either(TryToGetTheId(...))
        .Or(TrySomethingElseToGetTheId(...));

    return result.Id;
}

Что я действительно хотел бы сделать, так это неявное преобразование в T без присваивания:

public Guid GetPropertyId(...)
{
    return 
        //This is the part that I want to be implicitly cast to a ServiceResult
        Select
        .Either(TryToGetTheId(...))
        .Or(TrySomethingElseToGetTheId(...))
        //Then I want to access this property on the result of the cast
        .Id;
}

Однако указанный синтаксис не работает - мне нужно либо присвоить его переменной, либо явно привести его. Есть ли способ получить неявное приведение inline?

EDIT

Что я хочу сделать, это:

class Foo { 
    public int Fuh { get; set; } 
}

class Bar {
    private Foo _foo;
    public static implicit operator Foo (Bar bar)
    {
        return bar._foo;
    }
}

//What I have to do
Foo bar = GetABar();
DoSomethingWith(bar.Fuh);

//What I want to do
DoSomethingWith(GetABar().Fuh);

Ответы [ 7 ]

5 голосов
/ 13 сентября 2010

Хотя верно, что неявное приведение здесь не сработает, вы могли бы добиться большего успеха, чем явное приведение, добавив свойство Value к Select<T>. Тогда ваше выражение будет Select.[operations].Value.Id, которое все еще читается достаточно хорошо.

5 голосов
/ 14 сентября 2010

У меня вопрос: при использовании оператора доступа к элементу («точка»), есть ли способ сказать компилятору искать как члены Bar, так и тип, в который Bar неявно конвертируется?

Да, есть два способа сделать это.

Первое называется «наследование». Вы делаете это так:

class Bar : Blah { ... }
...
Bar bar = new Bar();
bar.Whatever();  // member access will look up members of both Bar and Blah.
Blah blah = bar; // bar is implicitly convertible to Blah.

Это говорит компилятору "когда вы ищите членов Bar, ищите также членов Blah". Он также сообщает компилятору, что экземпляры Bar неявно преобразуются в тип Blah.

Blah может быть классом или типом интерфейса.

Второй называется "классовые и интерфейсные ограничения для параметров типа". Вы намекаете компилятору так:

void M<T>(T t) where T : Blah
{
    t.Whatever(); // member access looks up members of Blah on t
    Blah blah = t; // t is implicitly convertible to Blah

Теперь t неявно конвертируется в Blah, и доступ к элементу t будет включать в себя элементы, объявленные в Blah.

Опять же, Blah может быть интерфейсом или типом класса.

В C # нет других способов повлиять на поиск членов в типе Bar, чтобы добавленные члены объявлялись в типе Blah, где Bar неявно конвертируется в Blah.

2 голосов
/ 13 сентября 2010

Я не думаю, что это выполнимо; какой-то оператор должен войти в игру, чтобы вызвать кастинг. Компилятор / среда выполнения не могут просто «знать», что вы хотите, чтобы он был типа T, вы должны как-то проинструктировать его сделать это.

1 голос
/ 14 сентября 2010

Мне кажется, я вижу твою проблему. Or возвращает Select, а не ServiceResult, поэтому компилятор не может знать, что вы ожидаете получить свойство Id от объекта ServiceResult. Как это могло? Должен ли он увидеть, что нет свойства Select.Id, и начать поиск возможных неявных преобразований, чтобы проверить, есть ли у одного из них свойство с именем Id?

Вот несколько вариантов:

public Guid GetPropertyId(...) 
{ 
    return  ((ServiceResult)
        Select 
        .Either(TryToGetTheId(...)) 
        .Or(TrySomethingElseToGetTheId(...)))
        .Id; 
} 

или

class Select
{
    public ServiceResult AsServiceResult()
    {
        return (ServiceResult)this;
    }
}

public Guid GetPropertyId(...) 
{ 
    return  
        Select 
        .Either(TryToGetTheId(...)) 
        .Or(TrySomethingElseToGetTheId(...)) 
        .AsServiceResult()
        .Id; 
} 

или

class Select
{
    public Guid Id { get { return ((ServiceResult)this).Id; } }
}

public Guid GetPropertyId(...) 
{ 
    return  
        Select 
        .Either(TryToGetTheId(...)) 
        .Or(TrySomethingElseToGetTheId(...)) 
        .Id; 
}
1 голос
/ 13 сентября 2010

Если вы хотите поместить преобразование в guid, вы можете использовать метод расширения

public static Guid ToGuid(this string id)
{
    return new Guid(id);
}

public Guid GetPropertyId(...)
{
    return Select
        .Either(TryToGetTheId(...))
        .Or(TrySomethingElseToGetTheId(...))
        .Id
        .ToGuid();
}

Я не думаю, что это действительно что-то добавляет, но в моем pov лучше читать.

0 голосов
/ 13 сентября 2010

Если вы собираетесь работать только с определенными типами, вы можете добавить методы или свойства оболочки. Если у myThing есть метод типа «Bar», который возвращает «self.internalThing.Bar», то можно было бы поддерживать иллюзию, что выражение, возвращающее myThing, действительно возвращает выражение его обернутого типа. Однако я не знаю, как это сделать в общем случае. Для будущих версий C # и VB может быть полезно разрешить «обёртывание» одного свойства в классе, но я не знаю ни одного способа сделать это в существующем языке.

0 голосов
/ 13 сентября 2010

Вы пробовали

public Guid GetPropertyId(...)
{
    return new Guid(Select
        .Either(TryToGetTheId(...))
        .Or(TrySomethingElseToGetTheId(...))
        .Id);
}
...