|
In this case, I don't care and don't need to know.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Never thought about using Google for such basic researches ?
Patrice
“Everything should be made as simple as possible, but no simpler.” Albert Einstein
|
|
|
|
|
I was thinking about the design I posted here. So I decided to create a prototype. I welcome your thoughts.
Service Base Class
public abstract class ServiceBase
{
public enum States
{
Cancelled,
Processing,
Stopped
}
#region Properties
public States CurrentState { get; protected set; }
public Guid ServiceId { get; private set; }
public CancellationTokenSource CancellationTokenSource { get; private set; }
#endregion
#region CTOR
public ServiceBase()
{
this.CurrentState = States.Stopped;
this.ServiceId = Guid.NewGuid();
this.CancellationTokenSource = new CancellationTokenSource();
}
#endregion
#region Abstract Methods
public abstract void Start();
public abstract void Stop();
#endregion
#region Public Methods
public void Cancel()
{
this.CurrentState = States.Cancelled;
CancellationTokenSource.Cancel();
}
#endregion
#region Private Methods
protected bool CheckIfCancelled(bool throwIfCancelled = false)
{
bool cancelled = false;
var token = CancellationTokenSource.Token;
if (token.IsCancellationRequested)
{
cancelled = true;
if (throwIfCancelled)
{
token.ThrowIfCancellationRequested();
}
}
return cancelled;
}
#endregion
}
The goal is here to have this base class create and store its own CancellationTokenSource. Then, calling Cancel() on it would allow callers from outside to cancel it. Cancelling is to cancel the Task that it's running it, where Start() and Stop() control the service's procssing.
Service Implementation
Here's an implementation example of that class:
public class MyService : ServiceBase
{
#region Public Methods
public override void Start()
{
CheckIfCancelled(true);
CurrentState = States.Processing;
while (CurrentState == States.Processing)
{
if (CheckIfCancelled())
{
break;
}
}
}
public override void Stop()
{
CurrentState = States.Stopped;
}
#endregion
}
The purpose of this design is to wrap the Cancel code in the base class, so simple implementations like this only need a simple method call to figure out if it's been cancelled.
Usage
The, the Windows Service, which is hosting all the services, looks like this
private static List<ServiceBase> _services;
static void Main(string[] args)
{
_services = new List<ServiceBase>();
<pre>
for (int x = 0; x < 100; x++)
{
MyService service = new MyService(); // See the CTOR of ServiceBase
_services.Add(service);
}
// Start each service
foreach (var service in _services)
{
Task.Factory.StartNew(() =>
{
service.Start();
}, service.CancellationTokenSource.Token);
}
}
Notice that the Task.Factory.StartNew gets the Cancellation Token from the service class.
Stopping or Cancellign
Later on, when it comes time to stop processing, or to Cancel a task, I can do the following:
private static ServiceBase GetServiceById(Guid serviceId)
{
var service = _services.Where(x => x.ServiceId ==1 serviceId).FirstOrDefault();
return service;
}
var s1 = GetServiceById(_services.FirstOrDefault().ServiceId);
s1.Stop();
var s2 = GetServiceById(_services.Last().ServiceId);
s1.Cancel();
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Your Start method does not return. Use it to create the internal thread and start that thread.
private bool _keepRunning;
public void Start()
{
_keepRunning = true;
Thread thread = new Thread(MainFunction);
thread.Start();
}
private void MainFunction()
{
while(_keepRunning)
{
try
{
}
catch(Exception ex)
{
}
Task.Delay(some time);
}
}
public void Stop()
{
_keepRunning = false;
} You don't need a CancellationToken here; a CancellationToken is used for cancelling a thread in circumstances where you do not have other possibilities of stopping it; consequently, it must be available outside of the thread, too: it must be shared between the thread to be cancelled and the item requesting the cancellation.
Also, you can now start your services in a "common" for/foreach loop.
|
|
|
|
|
Thanks for your input. Interesting approach.
I guess I was thinking that I wanted the Windows Service to create the threads. I'm trying to make this design so that other developers don't have to worry about the threading. They will only need to code up the Start() method with the services' code and leave the threading portion to the Windows Service.
As far as cancelling goes... I'm trying to make a clear distinction between cancelling a thread for the purpose of uninstalling a service, and stopping it which really means "pause" its processing.
These services will be loaded and launched via a scheduler process, so that's why I put the start portion in the Windows Service and included the CancellationToken.
Bernhard Hiller wrote: it must be shared between the thread to be cancelled and the item requesting the cancellation.
Note that the token is retrieved off the Service itself
foreach (var service in _services)
{
Task.Factory.StartNew(() =>
{
service.Start();
}, service.CancellationTokenSource.Token);
}
So, while I like your idea, is there any reason that you can see why my design wouldn't work?
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
You've been putting a lot of work into this, and I'm glad to see that you have a product gelling.
I have to agree with Bernhard on his point about external tokens as a control measure.
I have also have gone down this road, and I've found that having some event hooks for individual component recycling and monitoring go a long way to making this sort of host service work more effectively.
"There are three kinds of lies: lies, damned lies and statistics."
- Benjamin Disraeli
|
|
|
|
|
Something I would consider, if I were you, is some form of heartbeat capability. Basically, you would use the heartbeat to determine whether or not a service is alive.
This space for rent
|
|
|
|
|
By heartbeat I assume you mean some kind of ping from the server into the client?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
I also wanted to ask what are the best frameworks to improve the appearance of buttons. Microsoft has made no effort in this regard. I use DevComponents DotNetBar but it seems that few people use it and wanted to know what you are using.
modified 29-Mar-17 7:19am.
|
|
|
|
|
Regequion wrote: Microsoft has made no effort in this regard. I prefer plain WinForms, because of the effort they have made.
It is a very recognizable button, and it will look like that uniformly across all applications. There is no guessing involved for the user on what the control would do or how to use it.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Have you looked at WPF? With WPF you can do anything you want to any element, so you can create the look and feel you need with very little work.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
This is a bit long but I really need some validation on my project. I would appreciate your thoughts on the overall design and also my questions at the end
Overview
I'm creating an app that will run individual assemblies (called "Services") in a Windows Service. The Windows Service will really just be a host for the service classes that can be run according to a schedule. A "Service" is just an an assembly that implements an interface. Its run duration is determined by a schedule. It can run one time, or repeatedly according to scheduling options.
The Windows Service will be running on remote instruments around the world. The Windows Service will listen to a SignalR service running on our server that will all us to install, start, stop, disable, remove, or change the schedule for any or all of the "Services" on a particular instrument.
An example of a use case is diagnostics. A service will execute every 5 minutes to query a local DB for error records, then report its findings back to our server. The will be other use cases as well.
Details
On startup of the Windows Service the app will load services from a 'manifest'. This file is just a serialized list of services to load and when to run them. If an assembly is not runable (because the schedule says not to run, or it's been disabled), the it's not loaded.
Here's the manifest, not including the scheduling details.
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfService xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Service>
<ServiceName>TestClass1</ServiceName>
<ServiceId>59182d7e-8874-4d40-bdba-44936bcce815</ServiceId>
<AssemblyName>Services.Test.dll</AssemblyName>
<State>Ready</State>
</Service>
</ArrayOfService>
Assemblies that can be run will implement this interface
public interface IService
{
event EventHandler<ServiceMessageModel> StatusChanged;
string ServiceName { get; set; }
Guid ServiceId { get; set; }
ServiceState State { get; set; }
void Start();
void Stop();
}
For each item in the manifest the app will create an instance of each class and store that reference in a list:
private void LoadServices()
{
_services = new List<IService>();
string[] files = Directory.GetFiles(_servicesLocation, "*.dll");
foreach (string file in files)
{
var assemblyExistsInManifest = _manifest.Where(x => file.Contains(x.AssemblyName)).Any();
if (assemblyExistsInManifest)
{
Assembly assembly = Assembly.LoadFrom(file);
var types = assembly.GetTypes();
foreach (Type type in types)
{
var typeExistsInManifest = _manifest.Where(x => type.ToString().Contains(x.ServiceName)).Any();
var implementsInterface = typeof(IService).IsAssignableFrom(type);
if (typeExistsInManifest && implementsInterface)
var serviceClass = Activator.CreateInstance(type) as IService;
serviceClass.StatusChanged += ServiceClass_StatusChanged;
_services.Add(serviceClass);
}
}
}
}
}
Next, the list is iterated and each service in it is started in its own thread
private void StartAllServices()
{
foreach (var service in _services)
{
Task.Factory.StartNew(() =>
{
service.Start();
});
}
}
This is the basic idea and some code that I already have in place.
Questions
1) Cancelling / Pausing. I need to be able to stop a service from further processing. This could mean the service remains loaded but does not continue processing, or it could mean actually cancelling the task the service is running is so it can be unloaded.
This means that I would need to store separate CancellationTokenSource instances for each service as they're loaded in the StartService method above so that I can call Cancel() on a task for a specific service. If I defined the CancellationTokenSource as a property on the IService interface, then the StartServices method could create the CancellationTokenSource and assign it to the service at the time it is started.
This would allow my CancelService method to look like this
private void CancelService(Guid serviceId)
{
var service = _services.FirstOrDefault(x => x.ServiceId == serviceId);
if (service != null)
{
var ct = service.CancellationToken;
ct.Cancel();
}
}
Any thoughts on this?
2) Error Handling. Assuming an exception happens inside a service, this should not "crash" the service but instead set its State to Error and report that to the Host running on our server. The Windows Service portion should catch exceptions thrown by the services and report that back to the server. At no point should an exception cause either the Windows Service or any service to stop running. Any thoughts on this?
Summary
I'm VERY new to TPL and have little to no experience with it, so this design may be totally wrong. If so, please share your thoughts on a more stable approach. I would also appreciate any example code you may have.
Thank you
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
modified 28-Mar-17 14:42pm.
|
|
|
|
|
Starting the services: I'd prefer to have some kind of thread owned by a class implementing IService. In its Start method, that thread is created and started. Use a try...catch inside the loop of the main method (or in the Timer_Elapsed event, if you use a timer). Start/Stop can be controlled with a boolean member variable _keepRunning - just check it in the loop.
Error Handling: Add an "error event" to IService which gets subscribed by the Windows service. Ideally, it contains some information about how corrupted the state of that service is, such that the main service can decide if it's appropriate to unload it and create a new instance.
|
|
|
|
|
If I understand what you're saying.. it would mean creating the thread and error handling INSIDE the service classes, correct?
If so, it puts the responsibility on individual developers to follow the pattern to make it work.
Classes implementing IService only really need to have some basic info (Id, Name) and Start and Stop methods that the Windows Service can call into.
Am I understanding what you're saying here?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Can you provide a small example to illustrate what you're saying here?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
I'm creating a class instance using Activator.CreateInstance
var serviceClass = Activator.CreateInstance(type) as IInstrumentService;
_services.Add(serviceClass);
Then, later, I start each service. I want to catch any exceptions that occur in the service class. I was thinking about something like
private void StartAllServices()
{
foreach (var service in _services)
{
Task.Factory.StartNew(() =>
{
service.Start();
}).ContinueWith(task =>
{
if (task.Exception != null)
{
}
});
}
}
Any reason this would not work? Know of a better way to do this?
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Hi Kevin,
So does your service have a Stop() method as well? Normally a start method would be kind of asynchronous in nature, kicking off its own threads, opening network listeners and that sort of thing, so the test you have there looks like it would catch exceptions in just that part.
Or is it that Start() method is more of a Run()? By which I means it runs until completion, possibly days later?
A good catch all would be this (lifted from Visual Studio right in front of me):
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
Regards,
Rob Philpott.
|
|
|
|
|
Yes, the services have a Stop method. The services are independent of each other. They can each do entirely different things.
There will be a schedule in place that each services runs under. One may run only once, while another may run regularly at different days & times.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Just a quick note:
Passing a cancellation token to the Task.Factory.StartNew() will give you an external control for your child tasks.
Otherwise I've been playing with the same construct for a component host (except that I hydrate my components via a MEF ImportMany) and it works very well.
"There are three kinds of lies: lies, damned lies and statistics."
- Benjamin Disraeli
|
|
|
|
|
The as operator can return null . If you're not sure whether the type implements the interface, you should check for null before you add the service to the list. If you are sure, or you want an exception if it doesn't, then switch to using a cast instead:
var serviceClass = Activator.CreateInstance(type) as IInstrumentService;
if (serviceClass != null) _services.Add(serviceClass);
var serviceClass = (IInstrumentService)Activator.CreateInstance(type);
_services.Add(serviceClass);
You could pass TaskContinuationOptions.OnlyOnFaulted , which would remove the need to test whether the task has an exception.
.ContinueWith(task =>
{
}, TaskContinuationOptions.OnlyOnFaulted);
Also, Task.Run is generally preferred over Task.Factory.StartNew :
Task.Run vs Task.Factory.StartNew | Parallel Programming with .NET[^]
Task.Run(() => service.Start()).ContinueWith(...
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
How to read random line from text file that startswith * and lines below him that are related to him?
I have created to get random line with question using commands below, but after that it doesn't read lines below him that are related to him,instead of that he read lines from beggining!!!
var questions =
File.ReadLines(filePath)
.Where(line => line.StartsWith("*")).ToList();
var rng = new Random();
var myRandomQuestion = questions[rng.Next(questions.Count)].Substring(1);
label1.Text = myRandomQuestion;
My text file has 15000 lines.Lines with * character are questions and lines below them are terms relating to that question.
This is one part of file:
*Planete i njihovi sateliti
Zemlja=Mesec
Mars=Fobos
Jupiter=Io
Saturn=Titan
Uran=Titania
Neptun=Triton
Pluton=Haron
Merkur=Nema satelit
*Čuveni parovi iz umetnosti
Hamlet=Ofelija
Ruslan=Ljudmila
Zevs=Hera
Otelo=Dezdemona
Paris=Helena
Abelard=Eloiza
Paolo=Frančeska
Lanselot=Ginevra
*Latinski pojmovi
Kvalifikacija=Osposobljenost
Karantin=Izolacija
Radijacija=Zračenje
Ratifikacija=Potpisivanje
Racionalan=Razuman
Reakcija=Otpor
Realizacija=Ostvarenje
Rekapitulacija=Ponavljanje
*Poveži aktuelne predsednike sa državama u kojima vladaju.
Trajan Basesku=Rumunija
Karolos Papuljas=Grčka
Đorđo Napolitano=Italija
Tomas Hendrik Ilves=Estonija
Danilo Tirk=Slovenija
Dimitris Hristofias=Kipar
Tarja Halonen=Finska
Meri Mekelis=Irska
*Francuski gradovi
Bordeaux=Bordo
Auxerre=Okser
Toulouse=Tuluz
Nantes=Nant
Marseille=Marselj
Dijon=Dižon
Limoges=Limož
Chateauroux=Šatero
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections.Specialized;
using System.IO;
namespace Slagalica
{
public partial class Spojnice : MetroFramework.Forms.MetroForm
{
public Spojnice()
{
InitializeComponent();
}
string igra;
int j = 0;
Button[] button;
private void Spojnice_Load(object sender, EventArgs e)
{
Random r = new Random();
int indeks;
igra = "spojnice";
StreamReader sr = new StreamReader(igra + ".txt");
string[] niz1 = new string[8];
string[] niz2 = new string[8];
var questions = File.ReadLines(igra + ".txt").Where(line => line.StartsWith("*")).ToList();
var myRandomQuestion = questions[r.Next(questions.Count)].Substring(1);
label1.Text = myRandomQuestion;
button = new Button[] { button1, button2, button3, button4, button5, button6, button7, button8
,button9, button10, button11, button12, button13, button14, button15, button16 };
for (int i = 0; i < 8; i++)
{
string s = sr.ReadLine();
niz1[i] = s.Substring(0, s.IndexOf('='));
indeks = r.Next(1, 9);
bool ind = true;
while (ind)
{
ind = false;
switch (indeks)
{
case 1:
if (button9.Text == "")
button9.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 2:
if (button10.Text == "")
button10.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 3:
if (button11.Text == "")
button11.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 4:
if (button12.Text == "")
button12.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 5:
if (button13.Text == "")
button13.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 6:
if (button14.Text == "")
button14.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 7:
if (button15.Text == "")
button15.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
case 8:
if (button16.Text == "")
button16.Text = s.Substring(s.IndexOf('=') + 1);
else
{
indeks = r.Next(1, 9);
ind = true;
}
break;
}
}
}
for (j = 0; j < 8; j++)
button[j].Text = niz1[j];
for (j = 0; j < 16; j++)
button[j].BackColor = Color.DodgerBlue;
label1.BackColor = Color.DodgerBlue;
timer1.Enabled = true;
}
int ukupno = 0, pojam = 0;
private void RacunajZaDugme(Button dugme1, Button dugme2, string igr)
{
string s1 = dugme1.Text + "=" + dugme2.Text;
StreamReader sr = new StreamReader(igr + ".txt");
string s2 = sr.ReadLine();
while (!sr.EndOfStream)
{
s2 = sr.ReadLine();
if (s1 == s2)
{
ukupno += 4;
dugme1.BackColor = Color.Green;
dugme2.BackColor = Color.Green;
break;
}
if (s1 != s2)
{
dugme1.BackColor = Color.Red;
}
}
pojam++;
if (pojam == 8)
{
KrajIgre();
pojam = 0;
ukupno = 0;
}
}
private void KrajIgre()
{
timer1.Enabled = false;
button = new Button[] { button1, button2, button3, button4, button5, button6, button7, button8
,button9, button10, button11, button12, button13, button14, button15, button16 };
for (j = 0; j < 16; j++)
button[j].BackColor = Color.White;
for (j = 0; j < 16; j++)
button[j].Text = "";
MessageBox.Show("Osvojili ste " + ukupno + " poena ! ! !", "Kraj igre");
label1.Text = "";
label1.BackColor = Color.DarkBlue;
}
int i;
private void button2_Click(object sender, EventArgs e)
{
i = 2;
}
private void button3_Click(object sender, EventArgs e)
{
i = 3;
}
private void button4_Click(object sender, EventArgs e)
{
i = 4;
}
private void button5_Click(object sender, EventArgs e)
{
i = 5;
}
private void button6_Click(object sender, EventArgs e)
{
i = 6;
}
private void button7_Click(object sender, EventArgs e)
{
i = 7;
}
private void button8_Click(object sender, EventArgs e)
{
i = 8;
}
private void button9_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button9, igra);
break;
case 2:
RacunajZaDugme(button2, button9, igra);
break;
case 3:
RacunajZaDugme(button3, button9, igra);
break;
case 4:
RacunajZaDugme(button4, button9, igra);
break;
case 5:
RacunajZaDugme(button5, button9, igra);
break;
case 6:
RacunajZaDugme(button6, button9, igra);
break;
case 7:
RacunajZaDugme(button7, button9, igra);
break;
case 8:
RacunajZaDugme(button8, button9, igra);
break;
}
}
private void button10_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button10, igra);
break;
case 2:
RacunajZaDugme(button2, button10, igra);
break;
case 3:
RacunajZaDugme(button3, button10, igra);
break;
case 4:
RacunajZaDugme(button4, button10, igra);
break;
case 5:
RacunajZaDugme(button5, button10, igra);
break;
case 6:
RacunajZaDugme(button6, button10, igra);
break;
case 7:
RacunajZaDugme(button7, button10, igra);
break;
case 8:
RacunajZaDugme(button8, button10, igra);
break;
}
}
private void button11_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button11, igra);
break;
case 2:
RacunajZaDugme(button2, button11, igra);
break;
case 3:
RacunajZaDugme(button3, button11, igra);
break;
case 4:
RacunajZaDugme(button4, button11, igra);
break;
case 5:
RacunajZaDugme(button5, button11, igra);
break;
case 6:
RacunajZaDugme(button6, button11, igra);
break;
case 7:
RacunajZaDugme(button7, button11, igra);
break;
case 8:
RacunajZaDugme(button8, button11, igra);
break;
}
}
private void button12_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button12, igra);
break;
case 2:
RacunajZaDugme(button2, button12, igra);
break;
case 3:
RacunajZaDugme(button3, button12, igra);
break;
case 4:
RacunajZaDugme(button4, button12, igra);
break;
case 5:
RacunajZaDugme(button5, button12, igra);
break;
case 6:
RacunajZaDugme(button6, button12, igra);
break;
case 7:
RacunajZaDugme(button7, button12, igra);
break;
case 8:
RacunajZaDugme(button8, button12, igra);
break;
}
}
private void button13_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button13, igra);
break;
case 2:
RacunajZaDugme(button2, button13, igra);
break;
case 3:
RacunajZaDugme(button3, button13, igra);
break;
case 4:
RacunajZaDugme(button4, button13, igra);
break;
case 5:
RacunajZaDugme(button5, button13, igra);
break;
case 6:
RacunajZaDugme(button6, button13, igra);
break;
case 7:
RacunajZaDugme(button7, button13, igra);
break;
case 8:
RacunajZaDugme(button8, button13, igra);
break;
}
}
private void button14_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button14, igra);
break;
case 2:
RacunajZaDugme(button2, button14, igra);
break;
case 3:
RacunajZaDugme(button3, button14, igra);
break;
case 4:
RacunajZaDugme(button4, button14, igra);
break;
case 5:
RacunajZaDugme(button5, button14, igra);
break;
case 6:
RacunajZaDugme(button6, button14, igra);
break;
case 7:
RacunajZaDugme(button7, button14, igra);
break;
case 8:
RacunajZaDugme(button8, button14, igra);
break;
}
}
private void button15_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button15, igra);
break;
case 2:
RacunajZaDugme(button2, button15, igra);
break;
case 3:
RacunajZaDugme(button3, button15, igra);
break;
case 4:
RacunajZaDugme(button4, button15, igra);
break;
case 5:
RacunajZaDugme(button5, button15, igra);
break;
case 6:
RacunajZaDugme(button6, button15, igra);
break;
case 7:
RacunajZaDugme(button7, button15, igra);
break;
case 8:
RacunajZaDugme(button8, button15, igra);
break;
}
}
private void button16_Click(object sender, EventArgs e)
{
switch (i)
{
case 1:
RacunajZaDugme(button1, button16, igra);
break;
case 2:
RacunajZaDugme(button2, button16, igra);
break;
case 3:
RacunajZaDugme(button3, button16, igra);
break;
case 4:
RacunajZaDugme(button4, button16, igra);
break;
case 5:
RacunajZaDugme(button5, button16, igra);
break;
case 6:
RacunajZaDugme(button6, button16, igra);
break;
case 7:
RacunajZaDugme(button7, button16, igra);
break;
case 8:
RacunajZaDugme(button8, button16, igra);
break;
}
}
private void button1_Click(object sender, EventArgs e)
{
i = 1;
}
}
}
|
|
|
|
|
I'm not reading that entire code dump. Most of is probably not related to the problem.
You can't randomly seek to a specific "line" in a text file. You have to read through it, looking for the lines that start with "*". You can do ignore a random number of them but you might want to initialize some structure with the data from the file, reading through the entire file looking for the "*" lines and recording which line you found it. You can then pick one at random, and get the line number to skip to. You have to go back re-read the file, counting lines until you get to the one you want.
When you get the line with the "*" in it, you start reading and recording the data from each line, these are your answers, until you get to another line with a "*" in it.
|
|
|
|
|
I would have approached this issue 'a bit differently' - it depends if you want to have to whole file in memory, or what you wish to optimise ...
Assuming you dont want the whole file in memory, you read the file multiple times
In the OnLoad event for the WinForm, I would fire off a Background thread, to parse the file to extract all the questions, their start lines in the file, the number of answers, to a data structure - possible an array of struct(string question, int start line, int num answers) - this is your 'index'
once you have a random number, you get that element from the index and use the start line and num answers to locate the answers in the file
If you can keep the entire set of data in memory, then you parse the file to something like array of struct(string question, List<string> answers), which means once you have your random number, you can jump straight to the index and get the question and answers
|
|
|
|
|
I would suggest that the best thing to do would be to change your file format.
If you used each line to hold a single question, together with it's possible answers, separated by '|' characters:
Planete i njihovi sateliti|Zemlja=Mesec|Mars=Fobos|Jupiter=Io|Saturn=Titan|Uran=Titania|Neptun=Triton|Pluton=Haron|Merkur=Nema satelit
Čuveni parovi iz umetnosti|Hamlet=Ofelija|Ruslan=Ljudmila|Zevs=Hera|Otelo=Dezdemona|Paris=Helena|Abelard=Eloiza|Paolo=Frančeska|Lanselot=Ginevra
...
Then the problem becomes trivial:
Use File.ReadAllLines to read the whole file into an array of strings: each element of the array is a question together with it's answers, so selecting a random question is a trivial use of the Random class.
Each question and answer can be easily split out into an array of strings using string.Split - the first element of the array is the question, the rest of them are possible answers.
But do yourself a favour: stop using Visual Studio default names for everything - you may remember that "TextBox8" is the mobile number today, but when you have to modify it in three weeks time, will you then? Use descriptive names - "tbMobileNo" for example - and your code becomes easier to read, more self documenting, easier to maintain - and surprisingly quicker to code because Intellisense can get to to "tbMobile" in three keystrokes, where "TextBox8" takes thinking about and 8 keystrokes...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|