2

How do you instantiate a class without a public constructor (and without a static factory method). Specifically, I'm trying to use the .NET ElapsedEventArgs class with my custom timer class but creating new objects of it aren't possible. If you can't instantiate it how does System.Timers.Timer do it? Is the preferred way creating a similar class extending EventArgs?

ubi
  • 4,041
  • 3
  • 33
  • 50

2 Answers2

5

If you can't instantiate it how does System.Timers.Timer do it?

ElapsedEventArgs has a constructor, but it's internal, so only code declared in the same assembly can call it. You can see it on sourceof.net:

internal ElapsedEventArgs(int low, int high) {        
    long fileTime = (long)((((ulong)high) << 32) | (((ulong)low) & 0xffffffff));
    this.signalTime = DateTime.FromFileTime(fileTime);                        
}

Is the preferred way creating a similar class extending EventArgs?

Yes, I would say so.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • I never knew about the redirection of `sourceof.net` to `referencesource.microsoft.com`, cool! – Scott Chamberlain Jul 30 '15 at 01:05
  • Thanks @MarcinJuraszek any chance you could provide an answer to my other (somewhat related) question http://stackoverflow.com/questions/31690628/c-sharp-passing-data-to-the-original-thread-from-timer – ubi Jul 30 '15 at 01:36
2

It is possible to access non-public constructors using Reflection. You should keep in mind that Microsoft defined the class as internal and thus is free to redefine the constructor's signature without worrying about breaking your code, but I think with ElapsedEventArgs there is next to no chance that might happen.

The code to do it is demonstrated in the following sample:

using System;
using System.Reflection;
using System.Timers;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var constructorInfo = typeof(ElapsedEventArgs).GetConstructor(
                BindingFlags.NonPublic | BindingFlags.Instance, 
                null, new [] { typeof(int), typeof(int) }, null);
            var elapsedEventArgs = (ElapsedEventArgs)
                constructorInfo.Invoke(new object[] { 50, 50 });
        }
    }
}

The constructor signature for this class is convoluted, asking you to provide the high and low component of a DOS FILETIME structure; it's not very user friendly, and Reflection has VERY poor performance, so if you have a scenario where you are raising these events frequently if could be an issue. Reflection has a well-known (and well-earned) reputation for being slow; it should be used sparingly and only when it is your only alternative.

I would instead create my own EventArgs class with a simple constructor that has the current time in DateTime format, as shown in the following example:

using System;
using System.Timers;

public class MyElapsedEventArgs : EventArgs
{
    public MyElapsedEventArgs(DateTime fireTime)
    {
        FireTime = fireTime;
    }

    public DateTime FireTime { get; private set; }
}

You would then raise the event as so:

raise_event(new MyElapsedEventArgs(new DateTime.Now));

Easy breazy.

Jeff Prince
  • 658
  • 6
  • 13