简体   繁体   中英

C# Is it possible to create optional generic type constraints

I think I know the answer to this but I have the need to specify that a generic method can take a type based on two optional constraints. That being that T can be either one type or another.

public WebPage Click<T>(Func<WebPage> predicate) where T : LinkBase || FieldBase, new()
{
    WebDriver.FindElement(new T().LinkPath).Click();
    WebDriver.Wait();
    return predicate.Invoke();
}

I know that there is no such syntax currently, but is there a way to solve this without duplicating the method to constrain on both types? If not, is this in the realms of possibility for a future version of the language?

Doing this does not make sense if LinkBase and FieldBase do not have a common base or implement a common interface. And if they do, then you can simply use that one as the constraint.

I 'm saying it does not make sense because the very reason of using type constraints is to make sure that the actual generic type parameter used supports a known public interface (otherwise, you could have just made the whole thing to be non-generic and use object ). But if you have a hypothetical "or" constraint, how would the compiler be able to make sure that the code you write inside the generic will actually be meaningful for the type parameter that ends up being specified?

You're right, there's no way to pass in multiple types into a generic method.

Do you have control over both types? Can you make them each implement the same interface or abstract class?

One option for such problems is to apply the adapter pattern to your classes. In this case, they share the same properties, but not the same interface. (This is most useful if you do not control the source or it does not otherwise make sense for them to actually share an interface in normal scenarios.)

interface ILinkablePath
{
    string LinkPath { get; }
}

class LinkBaseAdapter : ILinkablePath 
{
    private LinkBase linkBase;

    public LinkBaseAdapter(LinkBase linkBase) 
    {
        this.linkBase = linkBase;
    }

    public string LinkPath { get { return this.linkBase.LinkPath; } }
}

You can write an adapter for each class you want to support. If they do not share a common interface, and possibly do not even share common methods or properties (in name, if not in execution), then you can use this technique when you wish to use the classes in the same way.

When you've done this, you are able to cleanly work with the adapting interface in your methods.

Otherwise, for your specific problem here, you can simply create different methods for each type you wish to support, or (if you're just working with the single property and not the rest of the object), just pass in the value of the property you want to use rather than the entire object itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM