C# Decorator Class using Default Class Version of Method

孤街浪徒 提交于 2019-12-11 04:52:19

问题


How do you leave a method the same as the default implementation if trying to decorate a class?

For example I am trying to add a property to a IWebDriver interface of the WebDriver class.

i.e.

public class MyWebDriver : IWebDriver
{
    private IWebDriver _driver;
    internal string _currentTest { get; set; }

    internal MyWebDriver(IWebDriver driver)
    {
        _driver = driver;
    }
}

Visual Studio IntelliSense had me implement the class (or interface) with a bunch of 'throw new NotImplementedException(); as the method body for the default WebDriver class methods.

i.e.

public IWebElement FindElement(By by)
{
    throw new NotImplementedException();
}

My question is if I don't want to adjust the behavior of these methods at all how do I implement that? Do I just leave it all as that line in the body or do I have to for example (if this is the case how would it be done for the FindElement method w/ a return type and parameter?):

public string CurrentWindowHandle
{
    get
    {
        return _driver.CurrentWindowHandle;
    }
}

public ReadOnlyCollection<string> WindowHandles
{
    get
    {
        return _driver.WindowHandles;
    }
}

public void Close()
{
    _driver.Close();
}

public void Quit()
{
    _driver.Quit();
}

I am just trying to add a string variable to the IWebDriver interface to keep track of which test is running.

EDIT: I found an alternative solution to my initial problem (which was proper logging of test results) that does not involve implementing any new functionality but rather placing try/catches in the correct places.

The solution to this question can be found in the marked answer as well as further below where @AlexanderWinter shows an example of an implementation including a way to retrieve data globally.

Thank you for all the help.


回答1:


Instead of extending the class, use external means to save the data. Something you can use is System.Runtime.CompilerServices.ConditionalWeakTable<TKey, TValue>.

You could use it in the following way:

class TestState
{
    public string Name { get; set; }
}

//initialization of some private field
ConditionalWeakTable<IWebDriver, TestState> myTestsByDriver = new ConditionalWeakTable<IWebDriver, TestState>();

...

//usage:
myTestsByDriver.GetOrCreateValue().Name = "My_First_Test";
//etc.



回答2:


IWebDriver has no default behavior, it has no behavior at all because it's an interface.

Interfaces has no content, only method and events prototypes. What you want is to inherit a class that implements this interface. So being already implemented, you can edit this class. If you were inheriting a class, you could override only the method you want and even call the base methods using base.callToABaseMethod()

Here's an example

public interface IAnimal //this has no behavior at all, it's an interface
{
    void eat();
    string Name { get; }
}

public class Animal //this has a behavior you can modify
{
    private string m_name;

    public Animal(string name)
    {
        this.m_name = name;
    }

    public void eat()
    {
        Console.WriteLine(m_name + " is eating !");
    }

    public string Name
    {
        get { return m_name; }
    }
}

public class Cat : Animal //this is your class that changes the behavior 
{                         //of the Animal class
    public Cat(string name) : base("Cat " + name) //we always need to call 
    { }                                           //a base constructor

    public override void eat() //here we are overriding the eat method
    {
        base.eat(); //calling the Animal eat method
        Console.WriteLine("He is eating fish !"); //editing the methd a bit
    }

    //no need to change the behavior of the Name property so we do nothing here
}

So find the class that has the default behavior you were talking about and inherit this one instead.

Hope it helps.

Edited to fit OP's needs:

In the case of a sealed class, you can't use this solution to edit the class. That's the point of a sealed class but C# has extension methods.

galenus' solution is fine but asking to make an extra call from somewhere in the application might be confusing for the class user. What you can do is mix extension methods and an external dictionary to make a complete simulation of an extra property. However, since extension properties doesn't exist in C#, you will have to create methods like GetProperty and SetProperty.

Here's an example:

/// <summary>
/// An extension from the sealed StringBuilder class that saves 
/// a color with a StringBuilder !
/// </summary>
public static class StringBuilderExt
{
    private static readonly Dictionary<StringBuilder, Color> colors = new Dictionary<StringBuilder, Color>();

    public static void SetColor(this StringBuilder builder, Color color)
    {
        colors[builder] = color;
    }

    public static Color GetColor(this StringBuilder builder)
    {
        if(colors.ContainsKey(builder))
        {
            return colors[builder];
        }

        return Color.Black;
   }
}

Example usage:

StringBuilder builder = new StringBuilder();

builder.SetColor(Color.Red);
Console.WriteLine(builder.GetColor().ToString());

But this is just a workaround, there's might be better ones. Try it and take what fits your needs the best.

Hope this will do




回答3:


Take a look at Extension methods to add functionality to functions instead of creating a wrapper around the entire class



来源:https://stackoverflow.com/questions/38959923/c-sharp-decorator-class-using-default-class-version-of-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!