简体   繁体   English

自定义属性以缓存 C# 中的只读属性值

[英]Custom Attribute to cache read only Property value in C#

I am trying to create a Custom Attribute capable of caching the value of a property, the type of the property is IWebElement which is somewhat expensive to create and could throw a NoSuchElement exception.我正在尝试创建一个能够缓存属性值的自定义属性,该属性的类型是IWebElement ,创建起来有点昂贵,并且可能引发NoSuchElement异常。

I have had some success implementing it like this:我已经取得了一些成功,像这样实现它:

protected Lazy<IWebElement> lazyWebElement;
protected virtual IWebElement cachedWebElement
{
    get
    {
        if (!lazyWebElement.IsValueCreated)
        {
            try
            {
                lazyWebElement = new Lazy<IWebElement>(() => driver.FindElement(By.Id("someElement")),
                    LazyThreadSafetyMode.PublicationOnly);
            }
            catch (NoSuchElementException)
            {
                throw new NoSuchElementException("someElement is not present in DOM");
            }
        }

        return lazyModal.Value;
    }
}

And what I would like to do is而我想做的是

[CachedWebElement]
protected virtual IWebElement cachedWebElement => driver.FindElement(By.Id("someElement"));

But Attribute does not allow complex types in it's constructor so I am not able to pass driver.FindElement(By.Id("someElement")) as a parameter.但是Attribute不允许在其构造函数中使用复杂类型,因此我无法将driver.FindElement(By.Id("someElement"))作为参数传递。

Having it cached would be great as right now I am using a backing field to save the value and the whole mechanism is lost if I override the property in a derived class.如果我在派生的 class 中覆盖该属性,那么缓存它会很棒,因为现在我正在使用支持字段来保存值,并且整个机制都会丢失。

Thank you.谢谢你。

You probably won't be very successful in your approach.你的方法可能不会很成功。 I mean, "just adding an attribute".我的意思是,“只是添加一个属性”。

After compiling, and during runtime, when you retrieve your object (ie when you do something like cachedWebElement.Displayed ), a get method is called.编译后和运行时,当您检索 object 时(即当您执行类似cachedWebElement.Displayed的操作时),将调用get方法。

When a project is build, all properties get get set accessor methods attached to them.构建项目时,所有属性都会get附加到它们的set访问器方法。 You can find out more here microsoft get keyword .您可以在此处找到更多信息microsoft get keyword Basically, the { get; set; }基本上, { get; set; } { get; set; } { get; set; } get compiled into functions, that you can't really "mess" with. { get; set; }编译成函数,你不能真正“搞砸”。 They are the default implementations of the compiler.它们是编译器的默认实现。 (if I got that right). (如果我猜对了)。

If you really want to follow this approach, take a look at this answer .如果您真的想遵循这种方法,请查看此答案

Now, to your problem现在,你的问题

  • If you want to have a "cache" for the elements properties, that's fine.如果您想为元素属性提供“缓存”,那很好。 You can create a class that will hold your elements for you, and before each FindElement() will check to see if it already exists.您可以创建一个 class 来为您保存元素,并且在每个FindElement()之前检查它是否已经存在。
  • If you also want to be able to "click" an element, that's impossible.如果您还希望能够“单击”一个元素,那是不可能的。 If you "FindElement" on a button, and then the page changes, and you want to Click() it from cache, that can't happen.如果您在按钮上“查找元素”,然后页面发生更改,并且您想从缓存中单击()它,那将不会发生。 The page changed.页面变了。 There is a reason that exception is thrown.抛出异常是有原因的。

I will assume that you want to "cache" only the properties/information about the element, and not actually interact with it.我将假设您只想“缓存”有关元素的属性/信息,而不是实际与之交互。

Then, we will just have to create a "middle man" that will handle locating each element, but will also cache the elements we find.然后,我们只需要创建一个“中间人”来处理每个元素的定位,但也会缓存我们找到的元素。 If we ask them again, the class will give them to us from memory instead of "re-locating" them.如果我们再次询问,class 将从 memory 将它们提供给我们,而不是“重新定位”它们。 Let's do them as extension methods to be a little pretty:让我们将它们作为扩展方法来做一些漂亮的事情:

public static class ElementLocator
{
    private static readonly Dictionary<By, IWebElement> _CachedElements;

    public static IWebElement FindElementFromCache(this IWebDriver driver, By by)
    {
        if (_CachedElements.ContainsKey(by))
            return _CachedElements.Single(e => e.Key == by).Value;

        return driver.FindElement(by);
    }

    public static ReadOnlyCollection<IWebElement> FindElementsFromCache(this IWebDriver driver, By by)
    {
        if (_CachedElements.ContainsKey(by))
        {
            List<IWebElement> foundCache = _CachedElements.Where(e => e.Key == by)
                                                          .Select(e => e.Value)
                                                          .ToList();

            return new ReadOnlyCollection<IWebElement>(foundCache);
        }

        return driver.FindElements(by);
    }

    static ElementLocator()
    {
        _CachedElements = new Dictionary<By, IWebElement>();
    }
}

Then, you can simply do driver.FindElementFromCache(By.Id("someElement"));然后,您可以简单地执行driver.FindElementFromCache(By.Id("someElement"));

But this might not be the complete implementation.但这可能不是完整的实现。 Because, when searching for multiple elements, maybe a new one was added.因为,在搜索多个元素时,可能添加了一个新元素。 Instead of 4 elements you got a minute ago, now a new table row is added, and you have 5 elements.一分钟前你得到了 4 个元素,现在添加了一个新的表格行,你有 5 个元素。 The ElementLocator will try to see if there is anything in cache, will find 4 elements and return them to you without searching for the 5th one. ElementLocator 将尝试查看缓存中是否有任何内容,将找到 4 个元素并将它们返回给您,而不搜索第 5 个元素。

IMO, creating a caching mechanism MIGHT not be the best solution to your problem. IMO,创建缓存机制可能不是解决您问题的最佳方法。 It introduces (excuse my language) a shit-ton of problems that you will face down the road.它引入了(请原谅我的语言)你将面临的一大堆问题。

If you could present us with what the problem is, we could figure out something that might not involve caching.如果您可以向我们展示问题所在,我们可以找出可能不涉及缓存的问题。 I mean, don't say "how can I do this cache better" or "my caching has a problem", but let's see why you need a cache for elements to begin with.我的意思是,不要说“我怎样才能更好地做这个缓存”或“我的缓存有问题”,但让我们看看为什么你需要一个缓存来开始元素。

Best of luck to you!祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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