|
|
In my app, recently, I had to innovate some kind of daring concept.
Though, as it is perfectly syntactically correct, it did work fine.
This is not really a question here, more like a discussion here:
1. could I have made it more simple?
2. what do you think of it?
Enter async event!
Here is my code fragment that uses them
public event Func<Task> AlarmReceived
{
add { lock (this) eAlarmReceived += value; }
remove { lock (this) eAlarmReceived -= value; }
}
Func<Task> eAlarmReceived;
internal async Task OnAlarm()
{
if (eAlarmReceived == null)
return;
var eTasks = eAlarmReceived.GetInvocationList().Cast<Func<Task>>().ToList();
foreach (var t in eTasks)
await t();
}
modified 18-Jul-16 21:14pm.
|
|
|
|
|
It's an interesting concept.
What do you get when you cross a joke with a rhetorical question?
The metaphorical solid rear-end expulsions have impacted the metaphorical motorized bladed rotating air movement mechanism.
Do questions with multiple question marks annoy you???
|
|
|
|
|
Thanks!
I was kind of not quite surprised but almost that it worked!
|
|
|
|
|
eAlarmReceived += value;
You can treat Func<Task> as a multicast delegate? I had no idea.
I've been playing around with this, and I'm not sure of the usage:
static void Main(string[] args)
{
new Program();
Console.ReadLine();
}
public Program()
{
DoIt();
}
public async void DoIt()
{
AlarmReceived += () => new Task(() => Console.WriteLine("A"));
AlarmReceived += () => new Task(() => Console.WriteLine("B"));
await OnAlarm();
}
That compiles but "A" and "B" never print -- what's a simple example of adding tasks to AlarmReceived?
[edit]
Never mind, I see what I was doing wrong. This works:
AlarmReceived += () => Task.Run(() => Console.WriteLine("A"));
AlarmReceived += () => Task.Run(() => Console.WriteLine("B"));
Now on to your question...
Marc
|
|
|
|
|
Glad you learned something Marc!
|
|
|
|
|
If this:
AlarmReceived += () => Task.Run(() => Console.WriteLine("A"));
is the correct syntax, then this:
AlarmReceived -= () => Task.Run(() => Console.WriteLine("A"));
doesn't unwire the delegate because it's a new instance. This "simpler" case (the way I might have tried it, because I didn't know about the multicast thingy) also doesn't unwire the alarms:
static void Main(string[] args)
{
new Program();
Console.ReadLine();
}
public Program()
{
DoIt2();
Console.WriteLine("Waiting for alarms to process...");
}
List<Action> alarmActions = new List<Action>();
public void AddAlarm(Action act)
{
alarmActions.Add(act);
}
public void RemoveAlarm(Action act)
{
alarmActions.Remove(act);
}
public async void DoIt2()
{
Console.WriteLine("Adding alarms");
AddAlarm(() => Alarm1());
AddAlarm(() => Alarm2());
await Task.Run(() => ProcessAlarms());
Console.WriteLine("Removing alarms");
RemoveAlarm(() => Alarm1());
RemoveAlarm(() => Alarm2());
await Task.Run(() => ProcessAlarms());
}
public void ProcessAlarms()
{
alarmActions.ForEach(a => a());
}
public void Alarm1()
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Alarm 1");
}
public void Alarm2()
{
Console.WriteLine("Alarm 2");
}
The only way to remove them is to instantiate the Action so the action isn't an anonymous method.
Action a = () => Alarm1();
Action b = () => Alarm2();
Console.WriteLine("Adding alarms");
AddAlarm(a);
AddAlarm(b);
await Task.Run(() => ProcessAlarms());
Console.WriteLine("Removing alarms");
RemoveAlarm(a);
RemoveAlarm(b);
await Task.Run(() => ProcessAlarms());
That approach is risky because, as the previous example showed, you could certainly wire up the alarm as an anonymous method, and the unwary programmer might think you can unwire it that way too, but you can't.
In your case, because AlarmReceived is an event of Task, I don't see how you can get away from an anonymous method:
Task a = Task.Run(() =>
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine("A");
}));
AlarmReceived += () => a;
The funny thing is, this ACTS like the event has been unwired because the task has been run!
Given:
public async void DoIt()
{
Task a = Task.Run(() =>
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine("A");
});
<pre>
Task b = Task.Run(() => Console.WriteLine("B"));
AlarmReceived += () => a;
AlarmReceived += () => b;
Console.WriteLine("Processing alarms");
await OnAlarm();
Console.WriteLine("Unwire");
AlarmReceived -= () => a;
AlarmReceived -= () => b;
await OnAlarm();
}
internal async Task OnAlarm()
{
if (eAlarmReceived == null)
{
Console.WriteLine("eAlarmReceived is null!");
return;
}
var eTasks = eAlarmReceived.GetInvocationList().Cast<Func<Task>>().ToList();
if (eTasks.Count == 0)
{
Console.WriteLine("Nothing to do!");
}
else
{
Console.WriteLine("Tasks = " + eTasks.Count);
foreach (var t in eTasks)
await t();
}
}</pre>
The output is:
Processing alarms
B
Tasks = 2
Waiting for alarms to process...
A
Unwire
Tasks = 2
But again, the use of the anonymous method for AlarmReceived means you can't unwire it.
Am I doing something different in the use case than you are?
Marc
|
|
|
|
|
Marc Clifton wrote: But again, the use of the anonymous method for AlarmReceived means you can't unwire it.
The same problem applies to regular events as well.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
|
I guess it mean aDelegate - aDelegate does something different that an Equal() comparison of its invocation list against the argument!
While it does something different. I am not sure what it is...
|
|
|
|
|
Stranger and stranger:
If I replace my original Source property implementation:
public AlarmSource Source
{
get { return source; }
set
{
Func<Task> handler = () => Task.Run(() => Console.WriteLine(Name));
if (source != null)
source.AlarmReceived -= handler;
source = value;
if (source != null)
source.AlarmReceived += handler;
}
}
AlarmSource source;
With this one
public AlarmSource Source
{
get { return source; }
set
{
if (source != null)
source.AlarmReceived -= () => Task.Run(() => Console.WriteLine(Name));
source = value;
if (source != null)
source.AlarmReceived += () => Task.Run(() => Console.WriteLine(Name));
}
}
AlarmSource source;
I do have the same bug as you have (i.e. the event handler is not removed).
This is relatively strange and I don't quite know what is happening...
But it is now narrowed down to a pretty strange syntax issue...
|
|
|
|
|
It is important to notice that you cannot easily unsubscribe from an event if you used an anonymous function to subscribe to it. To unsubscribe in this scenario, it is necessary to go back to the code where you subscribe to the event, store the anonymous method in a delegate variable, and then add the delegate to the event. In general, we recommend that you do not use anonymous functions to subscribe to events if you will have to unsubscribe from the event at some later point in your code.
I suspect your first sample works because you only have one anonymous method, which translates to a single hidden method:
Func<Task> handler = HiddenAnonymousMethod1;
if (source != null)
source.AlarmReceived -= handler;
source = value;
if (source != null)
source.AlarmReceived += handler;
In the second example, even though both anonymous methods are the same, they are probably translated to two different hidden methods:
if (source != null)
source.AlarmReceived -= HiddenAnonymousMethod1;
source = value;
if (source != null)
source.AlarmReceived += HiddenAnonymousMethod2;
You'd need to check the IL to verify that this is the case.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks Richard! That is a solid explanation here!
I might check it out!
|
|
|
|
|
If you have multiple handlers subscribed to the event, they will run sequentially. I'd be inclined to let them run in parallel.
You should probably also add a lock around the code that retrieves the list of handlers, to match the lock in the add / remove accessors.
private static readonly Task CompletedTask = Task.FromResult(true);
private IEnumerable<Func<Task>> GetAlarmInvocationList()
{
lock (this)
{
Func<Task> handler = eAlarmReceived;
if (handler == null) return null;
return handler.GetInvocationList().Cast<Func<Task>>().ToList();
}
}
internal Task OnAlarm()
{
var eTasks = GetAlarmInvocationList();
if (eTasks == null) return CompletedTask;
var tasks = eTasks.Select(fn => fn());
return Task.WhenAll(tasks);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
well.. it depends.. I wondered about running it in parallel too.
But I went against it.
In my case this is mobile phone code stuff. It mostly run sequentially. It just happen that some method are async....
As to the firing without lock... In fact you are right, I stupidly left a bug I knew about...
Juts because lock bothered me and the solution I had iin mind bother me too...
The solution I had in mind:
var eTasks = handler?.GetInvocationList()?.Cast<Func<Task>>()?.ToList();
if (eTask == null)
return;
foreach (var t in eTask)
await t();
The null invocation parameter.... might take care of multithread access issues, I think....
|
|
|
|
|
For example the code below
Pipeline pipeline = runspacee.CreatePipeline();
pipeline.Commands.Add(command1);
pipeline.Commands.Add(command2);
var exResults = pipeline.Invoke();
powershell.AddCommand("set-adserversettings")
.AddParameter("viewentireforest", true)
.AddParameter(";");
powershell.AddCommand("set-userphoto")
.AddParameter("Identity", tbxName.Text)
.AddParameter("picturedata", displayedImage)
.AddParameter("DomainController", "12-34-56-01.XXX.XXX.XXXX.XXX")
.AddParameter("confirm ", false)
.AddParameter(";");
|
|
|
|
|
... and your question is?
|
|
|
|
|
How can I run the code above in C#?
I can run single powershell commands, but nothing requiring multiple commands such as the example code I posted.
For example, I could run set-userphoto, but if I have to import a module or set-adserversettings, I cannot get that to work?
|
|
|
|
|
What is contained in command1 and command2 in your code snippet? And what results or errors do you see?
|
|
|
|
|
I should have removed the command1 and command2, those were just additional ways of me trying the same thing. Sorry about that.
Error message is
Quote: - The term 'Import-Module' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. - System.Management.Automation - at System.Management.Automation.PowerShell.CoreInvoke[TOutput](IEnumerable input, PSDataCollection`1 output, PSInvocationSettings settings) at System.Management.Automation.PowerShell.Invoke(IEnumerable input, PSInvocationSettings settings) at System.Management.Automation.PowerShell.Invoke(IEnumerable input) at System.Management.Automation.RemotePipeline.Invoke(IEnumerable input) at System.Management.Automation.Runspaces.Pipeline.Invoke() at exchangePictureUpdater.exchangePictureUpdater.btnAddReplace_Click(Object sender, EventArgs e) - Void CoreInvoke[TOutput](System.Collections.IEnumerable, System.Management.Automation.PSDataCollection`1[TOutput], System.Management.Automation.PSInvocationSettings) |
Command command1 = new Command("set-adserversettings");
CommandParameter parameter1 = new CommandParameter("viewentireforest", true);
command1.Parameters.Add(parameter1);
Command command2 = new Command("set-userphoto");
CommandParameter parameter2a = new CommandParameter("identity", tbxName.Text);
CommandParameter parameter2b = new CommandParameter("picturedata", displayedImage);
CommandParameter parameter2c = new CommandParameter("domaincontroller", "adfadfadf.com");
CommandParameter parameter2d = new CommandParameter("confirm", false);
command2.Parameters.Add(parameter2a);
command2.Parameters.Add(parameter2b);
command2.Parameters.Add(parameter2c);
command2.Parameters.Add(parameter2d);
Pipeline pipeline = runspacee.CreatePipeline();
pipeline.Commands.Add(command1);
pipeline.Commands.Add(command2);
|
|
|
|
|
|
results = pipeline.Invoke()
I need a working example of how to do this, and after that I can modify it to my own needs
|
|
|
|
|
Here is what I ended up doing, this allows me to create a script in a string with multiple commands, it processes 1 command at a time and then creates a runspace in c# and runs all of the powershell commands
string scriptText = @"$pw = convertto-securestring -AsPlainText -Force -String '<password>'; $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist '<domain>\<username>', $pw; $session = new-pssession -ConfigurationName Microsoft.Exchange -ConnectionUri '<server url>/powershell' -credential $cred; import-pssession $session; Set-ExecutionPolicy bypass -confirm:$false -force; $pic = ([System.IO.File]::ReadAllBytes('" + <picture file name> + "')); set-userphoto -identity <username> -picturedata $pic -domaincontroller '<dc fqdn>' -confirm:$false;";
runExchangeShellScript(scriptText);
private string runExchangeShellScript(string scriptText)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
Collection<PSObject> results = null;
try
{
results = pipeline.Invoke();
}
catch (Exception ex)
{
MessageBox.Show(ex.InnerException + " - " + ex.Message + " - " + ex.Source + " - " + ex.StackTrace + " - " + ex.TargetSite + " - " + ex.Data);
return "";
}
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
|
|
|
|
|
Suppose you have two forms: frmMain and frmPopup, on frmMain has 1 button (the button) click open frmPopup, on frmPopup many (button), you click on the button any frmPopup button will transmit data by the delegate of copper frmMain frmPopup before closing time announced new open yes / no of MessageBox.Show. When your done click the close button again poup frmPopup then MessageBox.Show on frmPopup, frmPopup refused to play, until the click buttons yes/no of MessageBox.Show, click the button here as yes / no, then frmPopup and MessageBox. Show it to close. I want to play a new fat frmPopup MessageBox.Show done. Their code has the following structure:
[code]
//Declare frmMain
public partial class frmMain : Form
{
private void getData(data)
{
if (conditions) Updata();
}
public frmMain()
{
InitializeComponent();
}
private void cmdfrmPopup_Click(object sender, EventArgs e)
{
frmPopup frm = new frmPopup(data);
frm.Data = getData;
frm.ShowDialog();
}
private void Updata()
{
DialogResult dlgRes;
string sAskUser = "Are you sure delete ?" ...
dlgRes = MessageBox.Show(sAskUser, "warning..", MessageBoxButtons.YesNo, Question);
if (dlgRes == DialogResult.OK)
{
}
}
}
//Khai báo frmPoup
public partial class frmPopup : Form
{
public frmPopup(nhandulieuden)
{
InitializeComponent();
}
private void cmdButton1_Click(object sender, EventArgs e)
{
Process();
}
private void cmdButton2_Click(object sender, EventArgs e)
{
Process();
}
private void cmdButton3_Click(object sender, EventArgs e)
{
Process();
}
private void cmdButton4_Click(object sender, EventArgs e)
{
Process();
}
private void Xuly()
{
this.Close();
transmissionOffrmMain(data);
}
private void cmdExit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
[/code]
through available tracks your household help this place.
|
|
|
|
|
If your message does not appear immediately, do not repost it again, and again, and again...
Your original post was sent to moderation by the automated spam detector, and it took a human (or in this case me) to decide to approve or reject it for publication. And to avoid giving you a severe kicking as a spammer, I had to let all of them through and then go clean up after you later. Which is a waste of my time!
If it doesn't post immediately, give it a few moments - we work as fast as we can, but we are all volunteers with other demands on our time.
I've deleted the three spare version of this...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|