I have the following code (from https://github.com/timabell/PageStructureBuilder ) and the ever knowledgeable ReSharper is suggesting that I make the DataFactoryCreatingPage() method static.
Is this safe, or would I be introducing a potential fault when used as an event handler?
public void Initialize(InitializationEngine context)
{
DataFactory.Instance.CreatingPage += DataFactoryCreatingPage;
DataFactory.Instance.MovedPage += DataFactoryMovedPage;
}
void DataFactoryCreatingPage(object sender, PageEventArgs e)
{
var parentLink = e.Page.ParentLink;
var page = e.Page;
parentLink = GetNewParent(parentLink, page);
e.Page.ParentLink = parentLink;
}
I can't think of any issues, but I'm wondering if I have a gap in my knowledge.
Thanks!
there's no reason why that handler should be static; the reason ReSharper suggests to make it static is probably because you're not using any instance variables within its body, so there's no harm, but event handlers shouldn't be static as they should be able to modify/use internal fields of the class that provides the handler's implementation
Resharper would do that if your method only uses the parameters passed in and does not access any member variables.
You dont have to do it.
Related
I'm struggling with exposing an event to C# from a class written in F#. When the Event is defined using a let binding this is no problem:
let myFSharpEvent = new Event<EventArgs>()
[<CLIEvent>]
member this.FSharpEvent = myFSharpEvent.Publish
member this.RaiseFSharpEvent e = myFSharpEvent.Trigger e
But when the Event is defined as private member it's compiling, but the event handler added at runtime is not executed.
member private this.myFSharpEvent = new Event<EventArgs>()
[<CLIEvent>]
member this.FSharpEvent = this.myFSharpEvent.Publish
member this.RaiseFSharpEvent e = this.myFSharpEvent.Trigger e
The C# code registering and calling the event looks like this:
class Program
{
static void Main(string[] args)
{
var fsObject = new FSharpClass();
Console.WriteLine(fsObject.ToString());
fsObject.FSharpEvent += FsObject_FSharpEvent;
fsObject.RaiseFSharpEvent(EventArgs.Empty);
fsObject.FSharpEvent -= FsObject_FSharpEvent;
Console.ReadLine();
}
private static void FsObject_FSharpEvent(object sender, EventArgs args)
{
Console.WriteLine("F# event was raised.");
}
}
I found no way to work with the let binding however, becaus in my real-world scenario my class inherits from a class written in C# and has not only a default constructor, but also a copy-constructor and a deserialization constructor that I need to override.
So the question is: Why is the let binding working, but the member private not.
I like to use SharpLab.io to quickly see how some F# compiles to c#.
//Your F#
let myFSharpEvent = new Event<EventArgs>()
//To c#
internal FSharpEvent<EventArgs> myFSharpEvent;
-----------
//Your F#
member private this.myFSharpEvent = new Event<EventArgs>()
//To c#
internal FSharpEvent<EventArgs> myFSharpEvent
{
get
{
return new FSharpEvent<EventArgs>();
}
}
You can see that every time you call the private version, you're getting a new event handler which is then discarded to GC.
I find typical OO stuff confusing in F# as well, but I think the main pattern is to use let bindings for backing fields and wrap them with member bindings:
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/members/let-bindings-in-classes
The problem is that every time you access myFsharpEvent as a member you are creating a new instance. The let binding is only creating it once.
So when you call Trigger it’s a different instance than the one you are publishing, which is why it’s in raising it in you c# code.
I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.
I think this article has the same problem with me. However, there's no workable solution for my case.
I'm using Windows Media Player ActiveX in my program.
For some reason, I don't want to add a reference of it and convert to AxHost automatically by IDE.
I create the instance by Activator and ProgID
protected const string WMP_PROG_ID = "WMPlayer.OCX.7";
private dynamic _wmp;
protected virtual bool init(){
try{
_wmp = Activator.CreateInstance(Type.GetTypeFromProgID(WMP_PROG_ID));
}
catch{ return false; }
return true;
}
I was tried to do this by Reflection, but I found that dynamic is suitable to my case.
Every property and method works alright, like these:
protected override bool setSpeed(float speed){
try{
_wmp.settings.rate = speed;
}
catch { return false; }
return true;
}
protected override int getLength(){
double res;
try{
res = _wmp.currentMedia.duration;
}
catch { return 0; }
return (int)(res * 1000);
}
Unfortunately, when I want to attach event like the article I indicated in the top, it got no work.
My code like this:
protected bool connectEvent(){
_wmp.StatusChange += new EventHandler(_wmp_StatusChange);
return true;
}
protected void _wmp_StatusChange(object sender, EventArgs e){
Console.WriteLine(_wmp.Status);
}
I've checked the type of event handler of StatusChange, it's EventHandler.
These codes compiled well, and I can load some music, play it, pause it, ...do anything I like.
But the StatusChange event never triggered.
I tried to set a break-point at connectEvent.
When run at _wmp.StatusChange += new EventHandler(...), the IntelliTrace give me some information.
Those information had written in Trad. Chinese, I think it means:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Could not apply operator "+=" to type System.Dynamic.DynamicObject and System.EventHandler
Even though there's an exception, but just like I said, compile was passed, everything still work -- except I could not listen event.
So, how can I attach event successfully in the dynamic object _wmp?
Any possible solution (like Reflection) is useful to me.
Also, in the case above, the handler type of StatusChange is EventHandler.
But if I want to handle PlayStateChange event, it is an "Unknown handle" if I don't add a reference of wmp.dll.
I hope the solution is suitable to this case, too.
Thanks everyone in advance for all of your support, and please forgive me for my poor English.
The generic strategy to turn a program that uses a COM object from early bound to late bound calling is to first write it early bound. IntelliSense will help you fall in the pit of success, ensuring that you use correctly named methods, pass the right kind of arguments and particularly useful to help you find out what the event handler signatures should look like.
Which produces this bit of test code:
void testEarlyBound() {
var wmp = new WMPLib.WindowsMediaPlayer();
wmp.StatusChange += new WMPLib._WMPOCXEvents_StatusChangeEventHandler(wmp_StatusChange);
}
void wmp_StatusChange() {
throw new NotImplementedException();
}
With the StatusChange event handler assignment and method body completely auto-generated by IntelliSense. Note the signature of the event handler, it is not an EventHandler. Just a method that returns void and takes no arguments, it matches the Action delegate type. Now you have a good shot at writing the late-bound version without the undiagnosable runtime exceptions:
void testLateBound() {
dynamic wmp = Activator.CreateInstance(Type.GetTypeFromProgID("WMPlayer.OCX"));
wmp.StatusChange += new Action(wmp_StatusChange);
}
I'll admit sometimes the deeper nuances of the keyword static escape me.
Here's what I'm seeing:
public partial class Default : CSSDEIStatusBase
{
private static Default _csWitWeb;
protected void Page_Load(object sender, EventArgs e)
{
//DoStuff
_csWitWeb = this;
//OtherStuff
}
public static void ForceLoadSyncOperation(int? index)
{
Default._csWitWeb.LoadSelectedSyncOperation(index);
}
}
The only references to ForceLoadSyncOperation are:
Default.ForceLoadSyncOperation(index);
or
Default.ForceLoadSyncOperation(null);
Both of these calls originate from:
public partial class DataOriginUserControl : System.Web.UI.UserControl
and are not located inside of static methods.
E.G:
protected void btnCancelSyncOperation_Click(object sender, EventArgs e)
{
this.lblErrorMessage.Text = string.Empty;
this.lblErrorMessage.Visible = false;
int index = _syncOperation.Sequence - 1;
Default.ForceLoadSyncOperation(index);
}
This all seems really quirky to me. Does this smell to anyone else? Not really sure how to untangle it, though, as I can't exactly create an instance of the Default page inside of a user control.
Thoughts? Thanks for reading.
protected void LoadSelectedSyncOperation(int? index)
{
SyncOperationConfiguration[] syncOperations = CSServiceClient.GetInterfaceConfiguration().SyncOperationConfigurations.ToArray();
PopulateSyncOperationsListView(syncOperations);
SyncOperationConfiguration syncOperation = null;
try
{
syncOperation = syncOperations[index.HasValue ? index.Value : 0];
}
catch
{
syncOperation = syncOperations[0];
}
ucDataOrigin.LoadSyncOperationData(syncOperation);
Session["ConfigMenuActiveIndex"] = 1;
menuConfiguration.Items[(int)Session["ConfigMenuActiveIndex"]].Selected = true;
mvwConfiguration.ActiveViewIndex = (int)Session["ConfigMenuActiveIndex"];
}
Presumably, the user control is contained within the Default page and the static member is being used as a shortcut to get the current instance of Default. I would've done it this way:
Default defaultPage = this.Page as Default;
if (defaultPage != null)
{
defaultPage.LoadSelectedSyncOperation(index);
}
Using a static member in this way is not safe. It opens up the door for race conditions. There is the potential risk that the user control is loaded in another page and calls LoadSelectedSyncOperation() on a separate request's instance of Default, thus wreaking all kinds of potential havoc.
I don't know what LoadSelectedSyncOperation does but this code looks weird. Whenever you click btnCancelSyncOperation you end up calling this method on some page, but you never know on which of them. It doesn't make much sense to me.
I would definitely say your concerns are valid. I can't think of any reason that this design would make sense, ever. This would throw a flag for me, too.
Based on your reply to my comment, if the Default.LoadSelectedSyncOperation is not dependent upon the Default page somehow, then I suggest it be refactored into a separate class (not an ASP.NET Page).
Whether it makes sense for the method or new class to be static or not is a separate concern and would be based on the logic contained within the method.
How can I call this method programmatically?
If I simple do KillZombies(), it says I don't have the correct parameters, but I don't know what parameters to specify when I'm just using code...
public static void KillZombies(object source, ElapsedEventArgs e)
{
Zombies.Kill();
}
Have you tried:
KillZombies(null, null);
Perhaps refactor your design:
public static void KillZombies(object source, ElapsedEventArgs e)
{
//more code specific to this event, logging, whathaveyou.
KillSomeZombies();
}
public static void KillSomeZombies()
{
Zombies.Kill();
}
//elsewhere in your class:
KillSomeZombies();
KillZombies(null, null);
However, I would question whether that's a good design.
You'd have to create the parameters and pass them through too. Why not just call the function directly by putting it in another function that is available for other classes to call? It'll make for much neater design.
i.e.
internal void MakeZombiesKill()
{
Zombies.Kill();
}
?
Your method signature requires two arguments. You cannot just call KillZombies(), you will need to pass the correct arguments to the method.
KillZombies(source, e);
If you do not have your source or e, you can simply pass null.
KillZombies(null, null);
You usually use the object from inside which you call the method as source (or null if static).
And set the ElapsedEventArgs to something relevant for the method. For ElapsedEventArgs it would be something like: new ElapsedEventArgs() { SignalTime = DateTime.Now}
KillZombies(this, new ElapsedEventArgs() { SignalTime = DateTime.Now});
If you don't really use source or e inside the method you can call it with null arguments.
KillZombies(null, null);
Technically speaking, you should be separating out the task from inside the event handler and have the event handler call the method containing the code you want run, this way you can call that code without tripping the event handler. However, if you want to trip the event handler programmatically:
KillZombies(this, new ElapsedEventArgs())
I however would break it out as is a frequently used best practice...