views:

201

answers:

7

Preserved question - see Edit at the bottom
I'm working on a small functional library, basically to provide some readability by hiding basic cyclomatic complexities. The provider is called Select<T> (with a helper factory called Select), and usage is similar to

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

and the library will take care of the short circuiting, etc. I also added an implicit conversion from Select<T> to T, so I can write

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

    return result.Id;
}

What I'd really like to be able to do is an implicit conversion to T without assignment:

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

However, the specified syntax doesn't work - I have to either assign it to a variable, or explicitly cast it. Is there a way to get an implicit cast inline?

EDIT

What I want to do is this:

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);
A: 

Did you try

public Guid GetPropertyId(...)
{
    return new Guid(Select
        .Either(TryToGetTheId(...))
        .Or(TrySomethingElseToGetTheId(...))
        .Id);
}
abhishek
That would technically work, but why allocate a new instance of a Guid, just to avoid a cast?
Coding Gorilla
This syntax doesn't work either - you still have to cast the result of the method chain to something. It's actually exactly the same as my last statement, but with the results sent to a Guid constructor.
arootbeer
If Id is already a Guid Type does it still need to Cast ?
abhishek
I think in his example, it's actually a Nullable<Guid>, so you have to actually return the Value member to get the _real_ Guid.
Coding Gorilla
+1  A: 

I don't think that is doable; some kind of operator has to come into play to cause the casting. The compiler/runtime can't just 'know' that you want it to be of type T, you have to somehow instruct it to do so.

Coding Gorilla
That's the answer I'm expecting to be correct at the end of the day. However, I figured I'd put the question out there in case I'm wrong!
arootbeer
A: 

If you want put the conversion to guid inline you can do a extension method

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

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

i don't think it really add something, but in my pov is better to read.

Leo Nowaczyk
+4  A: 

While it's true that an implicit cast won't work here, you could possibly do better than an explicit cast by adding a Value property to Select<T>. Then your expression would be Select.[operations].Value.Id, which still reads reasonably well.

zinglon
I ended up calling the property Result, but it's the same thing. I was hoping for a compiler hint, like supercat mentioned below, but this is apparently the best I'm gonna get.
arootbeer
A: 

If you are only going to be dealing with specific types, you could add wrapper methods or properties. If myThing has a method of type "Bar" which returns "self.internalThing.Bar", then one could maintain the illusion that that an expression returning a myThing was really returning an expression of its wrapped type. I know of no way to do that in the general case, however. It might be useful for future C# and VB versions to allow one property in a class to be "wrapped", but I know of no way to do that in the language as it exists.

supercat
VB6 had "default properties" that operated this way, but it was terribly inconvenient as it meant that you had to constantly decide whether you wanted the default property (`let`) or the actual object (`set`).
Gabe
@Gabe: The VB6 default properties weren't related to wrapping, I don't think (though maybe they could be used for that). If object foo's class had default property bar, the statement "foo = boz" was equivalent to "foo.bar = boz". What I'd like would be to declare that foo's class "wraps" bar, such that if bar supports field/property/method zaq, but foo does not, then saying foo.zig would be translated as foo.bar.zig. I don't think making bar a default property of foo in vb6 would do that; even if it did, the other costs would be annoying.
supercat
supercat: Sorry, I was a bit confused by your post. It sounds like you're talking about interface delegation (like described here: http://beust.com/java-delegation.html) in which case it would be a nice feature to add to the language.
Gabe
@Gabe: That's basically what I was thinking of, though I'd have been inclined to only allow a single delegation to avoid naming conflicts. I suppose if one only delegated to interfaces, the conflicts might not be so bad since interfaces can't change without a recompile. It might be useful to delegate to a class, though, and that would only be safe if it was the sole delegation; if one might want to delegate inherited methods of a class, one should make the delegating class be a generic class with the wrapped class as a type parameter.
supercat
A: 

I think I see your problem. Or returns a Select, not a ServiceResult, so there's no way that the compiler could know that you expect to get the Id property from a ServiceResult object. How could it? Should it see that there is no Select.Id property and start looking for possible implicit conversions to see if one of them has a property called Id?

Here are a few of your choices:

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

or

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

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

or

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

public Guid GetPropertyId(...) 
{ 
    return  
        Select 
        .Either(TryToGetTheId(...)) 
        .Or(TrySomethingElseToGetTheId(...)) 
        .Id; 
}
Gabe
I'd like for it to :)
arootbeer
+4  A: 

My question is: when using the member access ("dot") operator, is there a way to tell the compiler to look up both the members of Bar and a type to which Bar is implicitly convertible?

Yes, there are two ways to do that.

The first is called "inheritance". You do it like this:

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.

That tells the compiler "when you're looking up members of Bar, look up members of Blah too". It also tells the compiler that instances of Bar are implicitly convertible to type Blah.

Blah can be either a class or an interface type.

The second is called "class and interface constraints on type parameters". You hint to the compiler like this:

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

Now t is implicitly convertible to Blah, and member access on t will include members declared on Blah.

Again, Blah can be an interface or class type.

There are no other ways in C# to affect member lookup on a type Bar such that the added members are declared on a type Blah, where Bar is implicitly convertible to Blah.

Eric Lippert
Thanks for the confirmation. It would be nice in this case if the implicit cast operator would enable you to use Foo as Bar, but I can certainly see the complexities involved with that (especially as default behavior).
arootbeer