4

I have the following extension method for monitoring a variable and awaiting until it has a given expected value:

        public static async Task AwaitForValue<T>(this Func<T> valueGetter, T expectedValue, int? millisecondsToAwaitBetweenChecks = null, int? maxMillisecondsToAwait = null) 
            where T : struct
        {
            var timeToAwait = millisecondsToAwaitBetweenChecks ?? 20;
            if (maxMillisecondsToAwait.HasValue)
            {
                var stopWatch = new Stopwatch();
                stopWatch.Start();
                while (!valueGetter().Equals(expectedValue) || stopWatch.ElapsedMilliseconds >= maxMillisecondsToAwait)
                {
                    await Task.Delay(timeToAwait);
                }
            }
            else
            {
                while (!valueGetter().Equals(expectedValue))
                {
                    await Task.Delay(timeToAwait);
                }
            }    
         }

It is working just fine:

class Foo
        {
            private bool _flag;

            public async void DoWork()
            {
                Task.Run(() =>
                {
                    Thread.Sleep(5000);
                    _flag = true;
                });
                await new Func<bool>(() => _flag).AwaitForValue(true);
                Console.WriteLine(_flag);
            }
         }

I would like to define a ref extension method that gives me a delegate that returns me the current value of the ref variable, something like:

public delegate ref T RefFunc<T>();

This way my previous extension method could extend RefFunc<T> instead of Func<T> and be consumed, hypothetically, like the following:

_flag.ToRefFunc().AwaitForValue(true);

The problem is that I can not find a way to properly define ToRefFunc<T>(this ref T value); since ref is not allowed in lambdas. So, is there a way to define the following method?

public static RefFunc<T> ToRefFunc<T>(this ref T value) where T : struct
{
     //todo some day
}

Ideally it will look like this:

public static RefFunc<T> ToRefFunc<T>(this ref T value) where T : struct => new RefFunc<T>(() => value);

Any help would be appreciated

EDIT

The problem is not whether ref extension methods are allowed, from the "duplicate" question see this

EDIT 2 The anonymous delegate syntax does not work either:

public static RefFunc<T> ToRefFunc<T>(this ref T value) where T: struct
{
    return delegate { return value; }; 
}

Here I got: Cannot use ref, out, or in parameter 'value' inside an anonymous method, lambda expression, query expression, or local function

14
  • 1
    @Joelius in C# 7.2 you can actually define ref extension methods as long as they are extending structs
    – taquion
    Commented Aug 16, 2019 at 17:37
  • 1
    I'll take it back then :) Did not know that. The docs page doesn't cover that or am I blind? Didn't find it here either. Is there a docs-page for this?
    – Joelius
    Commented Aug 16, 2019 at 17:50
  • @Joelius that is actually weird, when I was about to use it I found this, however in the official docs for what is new in c# 7.2 there is nothing, see here
    – taquion
    Commented Aug 16, 2019 at 17:58
  • 1
    Not exactly what you're asking for, but you can create some ObservableBase class with this AwaitForValue() function. This helps with any scope issues you might encounter trying to access an arbitrarily scoped value type. And you can do something like AwaitForValue(nameof(_flag), true).
    – chill94
    Commented Aug 16, 2019 at 19:29
  • 1
    @taquion: Oh sorry, I misunderstood and thought you were trying to define an anonymous function with a ref return type. You're trying to close on a ref parameter in the calling scope. That just isn't allowed.
    – Ben Voigt
    Commented Aug 16, 2019 at 21:53

1 Answer 1

1

A ref parameter is something you can only use within your function, not something you can "save for later". That's because the ref parameter cannot keep its referent variable alive.

Consider using a reference type (C# parlance: class) to hold your data. Then you can have any number of handles to it, and they'll participate in garbage collection (object lifetime, memory compaction, etc). Unfortunately, this requires changing all users of the variable -- it's not something you can retrofit onto data in an existing object model.

1
  • That makes sense... uhh.. I was hoping to find a way.. Thanks man!
    – taquion
    Commented Aug 16, 2019 at 22:38

Not the answer you're looking for? Browse other questions tagged or ask your own question.