I'm facing a problem where my declared StreamWriter gets disposed when I try to use a method to write on my log file. Everything is working as expected, except when I run AttachPink or AttachBlue, from another class. Then the StreamWriter is disposed and I get a nullPointerException
class Logs : IDisposable
{
//other declarations
private StreamWriter HistoryWriter;
private int ReportInterval = 0;
public void NewHistory()
{
HistoryWriter = new StreamWriter(HistoryLocation + HistoryName + HistoryExtension);
PrepareHistory();
}
private void PrepareHistory()
{
HistoryWriter.WriteLine("<html><body bgcolor='#000000'>");
/*
* Insert initial HTML tags
*/
}
public void SendHistory()
{
HistoryWriter.WriteLine("</body></html>");
/*
* Final HTML tags
*/
HistoryWriter.Close();
if (ReportInterval > 0)
{
/*
* Upload File
*/
}
else
{
Debug.WriteLine("ERROR: Report Interval for History has not been set");
}
NewHistory();
}
public void AttachPink(String message, StreamWriter writer)
{
writer.Write(
"<font color='DA1BE0'>"
+ message
+ "</font>");
}
public void AttachBlue(String message, StreamWriter writer)
{
writer.Write(
"<font color='0C93ED'>"
+ message
+ "</font>");
}
public StreamWriter getHistoryWriter()
{
return HistoryWriter;
}
public void SetHistoryInterval(int interval)
{
ReportInterval = interval;
}
public void Dispose()
{
if (HistoryWriter != null)
{
HistoryWriter.Close();
HistoryWriter.Dispose();
HistoryWriter = null;
}
}
}
To use the methods I simply declare an instance of Logs class inside another class, like so:
class UsingLogs
{
Logs logs = new Logs();
logs.NewHistory();
logs.AttachBlue("my message", logs.getHistoryWriter());
}
I don't know how should I go for preserving classes variables state when accessing multiple methods.
I guess what you are looking for is the Singleton Pattern (http://en.wikipedia.org/wiki/Singleton_pattern)
a simple implementation of mine which you can reuse every time you need a singleton
public class Singleton<T> where T : class, new()
{
private static object sync = null;
private static volatile T i;
protected Singleton() { }
public static T I
{
get
{
if (i == null)
lock (sync)
if (i == null)
i = new T();
return i;
}
}
}
You can implement your Log class like this:
class Logs : Singleton<Logs>
{
... your code goes here
}
In your code, when you want to use the Logs class, you simply use:
Logs.I.AttachBlue(...);
Hope this helps :)
Related
Background
I'm in a need for a queued message broker dispatching messages in a distributed (over consecutive frames) manner. In the example shown below it will process no more than 10 subscribers, and then wait for the next frame before processing further.
(For the sake of clarification for those not familiar with Unity3D, Process() method is run using Unity's built-in StartCoroutine() method and - in this case - will last for the lifetime of the game - waiting or processing from the queue.)
So i have such a relatively simple class:
public class MessageBus : IMessageBus
{
private const int LIMIT = 10;
private readonly WaitForSeconds Wait;
private Queue<IMessage> Messages;
private Dictionary<Type, List<Action<IMessage>>> Subscribers;
public MessageBus()
{
Wait = new WaitForSeconds(2f);
Messages = new Queue<IMessage>();
Subscribers = new Dictionary<Type, List<Action<IMessage>>>();
}
public void Submit(IMessage message)
{
Messages.Enqueue(message);
}
public IEnumerator Process()
{
var processed = 0;
while (true)
{
if (Messages.Count == 0)
{
yield return Wait;
}
else
{
while(Messages.Count > 0)
{
var message = Messages.Dequeue();
foreach (var subscriber in Subscribers[message.GetType()])
{
if (processed >= LIMIT)
{
processed = 0;
yield return null;
}
processed++;
subscriber?.Invoke(message);
}
}
processed = 0;
}
}
}
public void Subscribe<T>(Action<IMessage> handler) where T : IMessage
{
if (!Subscribers.ContainsKey(typeof(T)))
{
Subscribers[typeof(T)] = new List<Action<IMessage>>();
}
Subscribers[typeof(T)].Add(handler);
}
public void Unsubscribe<T>(Action<IMessage> handler) where T : IMessage
{
if (!Subscribers.ContainsKey(typeof(T)))
{
return;
}
Subscribers[typeof(T)].Remove(handler);
}
}
And it works and behaves just as expected, but there is one problem.
The problem
I would like to use it (from the subscriber's point of view) like this:
public void Run()
{
MessageBus.Subscribe<TestEvent>(OnTestEvent);
}
public void OnTestEvent(TestEvent message)
{
message.SomeTestEventMethod();
}
But this obviously fails because Action<IMessage> cannot be converted to Action<TestEvent>.
The only way i can use it is like this:
public void Run()
{
MessageBus.Subscribe<TestEvent>(OnTestEvent);
}
public void OnTestEvent(IMessage message)
{
((TestEvent)message).SomeTestEventMethod();
}
But this feels unelegant and very wasteful as every subscriber needs to do the casting on it's own.
What i have tried
I was experimenting with "casting" actions like that:
public void Subscribe<T>(Action<T> handler) where T : IMessage
{
if (!Subscribers.ContainsKey(typeof(T)))
{
Subscribers[typeof(T)] = new List<Action<IMessage>>();
}
Subscribers[typeof(T)].Add((IMessage a) => handler((T)a));
}
And this works for the subscribe part, but obviously not for the unsubscribe. I could cache somewhere newly created handler-wrapper-lambdas for use when unsubscribing, but i don't think this is the real solution, to be honest.
The question
How can i make this to work as i would like to? Preferably with some C# "magic" if possible, but i'm aware it may require a completely different approach.
Also because this will be used in a game, and be run for it's lifetime i would like a garbage-free solution if possible.
So the problem is that you are trying to store lists of a different type as values in the subscriber dictionary.
One way to get around this might be to store a List<Delegate> and then to use Delegate.DynamicInvoke.
Here's some test code that summarizes the main points:
Dictionary<Type, List<Delegate>> Subscribers = new Dictionary<Type, List<Delegate>>();
void Main()
{
Subscribe<Evt>(ev => Console.WriteLine($"hello {ev.Message}"));
IMessage m = new Evt("spender");
foreach (var subscriber in Subscribers[m.GetType()])
{
subscriber?.DynamicInvoke(m);
}
}
public void Subscribe<T>(Action<T> handler) where T : IMessage
{
if (!Subscribers.ContainsKey(typeof(T)))
{
Subscribers[typeof(T)] = new List<Delegate>();
}
Subscribers[typeof(T)].Add(handler);
}
public interface IMessage{}
public class Evt : IMessage
{
public Evt(string message)
{
this.Message = message;
}
public string Message { get; }
}
I'm working with an C# .Net application that uses Cplex DLL's for an optimization operation, and during that operation I want to write status progress to a statusbar on the that initiated the operation.
This is the general layout of the specific form;
namespace ActResMain
{
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//...
cplex.Use(new Cplex_ContinuousCallback());
cplex.Solve()
}
public void Update_OptimizeStatusbarPanel(String strText)
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
//From here I want to edit the statusbar at FormOptimize. I can write progress to console without any problems, but cannot reach function "Update_OptimizeStatusbarPanel".
//If I include "FormOptimize formOpt = new FormOptimize" here, i get Visual studio exception on illegal window reference.
}
}
}
}
I have also tried invoking the Update_OptimizeStatusbarPanel function like this:
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
FormCollection fc = Application.OpenForms;
var mpc = fc[1];
Type type = mpc.GetType();
MethodInfo dynMethod = type.GetMethod("Update_OptimizeStatusbarPanel");
dynMethod.Invoke(mpc, new object[] { String.Format("Running Optimization: {0} iterations ", Niterations)});
}
}
But then I get an exception from visual studio stating that an object created by one thread cannot be modified from another thread.
Maybe this is something stupid that I have missed, but help is greatly appriciated
EDIT: I edited the code as per Mohammad Dehghans suggestion,
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
cplex.Use(new Cplex_ContinuousCallback(this));
cplex.Solve()
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
if (Niterations % 10 == 0)
{
_formOptimize.Update_OptimizeStatusbarPanel(0, String.Format("Running Optimization: {0} iterations ", Niterations), 0);
}
}
}
public void Update_OptimizeStatusbarPanel(short panelIndex, String strText, short severity)
{
if (statusBar1.InvokeRequired)
statusBar1.Invoke(new Action<short, string, short>(Update_OptimizeStatusbarPanel), panelIndex, strText, severity);
else
{
if (panelIndex == 0)
{
//...
statusBarPanel_0.Text = strText;
}
else if (panelIndex == 1)
{
//...
statusBarPanel_1.Text = strText;
}
statusBar1.Refresh();
}
}
}
But by doing that I apparently broke something, as the application just ..stops after statusBar1.Invoke() is called the first time. If I pause the debugger it says that cplex.Solve() is executing, but then nothing more happens.
First of all, you need to pass the instance of your form to the implemented callback class, so when the Main method is called, you have access to the exact instance that is being shown on the screen.
Secondly, you need to use Invoke method to update the UI controls from anther thread (I've not worked with CPLEX so far, but I guess the callback is invoked from another thread. That's usual).
Read this for more information.
The complete code could be:
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//Misc code
cplex.Use(new Cplex_ContinuousCallback(this)); // <-- passing `this`
cplex.Solve()
//Misc code
}
public void Update_OptimizeStatusbarPanel(String strText)
{
if (statusBarPanel_1.InvokeRequired)
statusBarPanel_1.Invoke(Action<string>(Update_OptimizeStatusbarPanel), strText);
else
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
//...
_formOptimize.Update_OptimizeStatusbarPanel(String.Format("Running Optimization: {0} iterations ", Niterations));
}
}
}
My Goal: I have HASP with whom I need to communicate using Serial port.
The thing is many functions from different threads may want to communicate to this HASP - and I want some synchronization to occur.
What I did: I created wrapper class called HASPClass. Looks like this:
class HASPCLass
{
SerialPort m_port;
HASPClass(..)
{
//.. Init some other properties
m_port.Open();
//..
}
void CustomWriteToHASP()
{
//.. Do something
m_port.Write(...);
}
void CustomReadHASP()
{
//.. Do something
m_port.Read(...);
}
void Close ()
{
//Some code to close m_port
};
}
Usage of this class would be:
Function1 from some thread:
HASPClass o = new HASPClass(..);
o.CustomWriteToHASP(..)
Function2 from other thread:
HASPClass o1 = new HASPClass(..);
o1.CustomReadHASP(..)
Problem1: Now if o didn't close the m_port - constructor of o1 will throw since port is open.
I want to avoid this and make o1 wait for o to finish job.
What I thought: Maybe I should make m_port static - and put static lock everywhere it is used in HASPClass, will it solve above problem? Also the constructor will be changed to only open static m_port if it is closed. Will this approach solve most of the problems I outlined before?
Update: My other problem is that different objects might specify different parameters (baud rate etc.) in constructor - so I encounter a problem :( since I have single static m_port. :(. What to do in such case?? (I could relax this requirement and say all objects will put same parameters in constructor, but will it help?)
A simple singleton pattern might look something like this:
class HASPClass
{
private static HASPClass _instance;
private HASPClass(..)
{
//.. Init some other properties
}
public static GetInstance(...)
{
// Note, if called with different parameters then this will be
// quite a bit more complicated
if (_instance == null)
{
_instance = new HASPClass(...)
}
return _instance;
}
}
Now when you call it, you'd do something like:
HASPClass o = HASPClass.GetInstance(..);
o.CustomWriteToHASP(..)
But...since you are multithreading, this pattern won't be safe. You'll need to implement some locking around the critical GetInstance section to ensure that you don't create more than one object. So you could do something like:
private static object lockObj = new object();
public static GetInstance(...)
{
// Note, if called with different parameters then this will be
// quite a bit more complicated
if (_instance == null)
{
lock (lockObj)
{
if (_instance == null)
{
_instance = new HASPClass(...)
}
}
}
return _instance;
}
Better than manually locking would be to use Lazy, but that might be complicated if you need to pass parameters. If (as I assume) those parameters are only ever passed once, you might want to have a separate initialization function that will store those parameters so you don't need to pass them when you get your instance.
If the parameters are the same every time, you could maybe try something like this:
class HASPClass
{
private static ParameterObject _parameters;
private static Lazy<HASPClass> _instance = new Lazy<HASPClass>(() =>
{
if (_parameters == null)
{
throw new InvalidOperationException("Can get instance before initializing");
}
return new HASPClass(_parameters);
});
public static HASPClass Instance
{
get { return _instance.Value; }
}
private HASPClass(ParametersObject parameters)
{
// create and populate your object using values from parameters
}
public static void Initialize(ParameterObject parameters)
{
if (_parameters != null)
{
// you might throw an exception here if this is not allowed
// Or you might drop and recreate your object if it is allowed
}
_parameters = parameters;
}
}
You may or may not need to have locking around Initialize, but the idea would be that you'd probably call Initialize first from a parent thread so that it never needs to be called again from any other thread.
class HASPCLass
{
static SerialPort m_port;
HASPClass(..)
{
lock(m_port)
{
if (!Initialized())
{
Initialize();
}
}
}
void Close ()
{
lock(m_port)
{
if (Initialized())
{
Uninitialize();
}
}
}
}
Here is one more variant of the code for you. It should work in any case. It reopens the port in case of different baud rate requested.
class HASPCLass
{
private static SerialPort m_port;
private static bool m_initialized;
private static int m_baudRate;
public HASPClass(int baudRate)
{
lock(m_port)
{
if (!m_initialized)
{
Initialize(baudRate);
}
}
}
private Initialize()
{
m_port.open(baudRate);
m_baudRate = baudRate;
m_initialized = true;
}
private Uninitialize()
{
m_port.close();
m_initialized = false;
}
private ReinitializeIfNeeded(int baudRate)
{
if (baudRate != m_baudRate)
{
Uninitialize();
Initialize(baudRate);
}
}
public void Read(int baudRate, out buff)
{
lock(m_port)
{
ReinitializeIfNeeded(baudRate);
m_port.Read(out buff);
}
}
public void Write(int baudRate, in buff)
{
lock(m_port)
{
ReinitializeIfNeeded(baudRate);
m_port.Write(buff);
}
}
public void Close()
{
lock(m_port)
{
if (m_initialized)
{
Uninitialize();
}
}
}
}
I am putting together a plugin framework with these requirements:
load/unload plugins at will
call methods in loaded plugins
raise callback events from plugin to the owner
To do this I am creating a new AppDomain, and loading the Plugin assemblies into this.
The implementation I have so far is working to a degree, but I believe I am creating an instance of the plugin in the local appDomain and also the new AppDomain.
When I load first, I get duplicate callback messages. When I load/unload multiple times I get multiple callback messages being added to the list. This indicates to me that I am loading up the plugin assembly not only remotely but also locally, and thus my "unload" mechanism is not operating as I would like. I would be grateful if anyone can tell me where I am going wrong.
I also understand I need to take the "life time" of the plugin into account but not sure where to implement this.
Thanks.
(1) I have a plugin interface
public interface IPlugin
{
string Name();
string Version();
string RunProcess();
// custom event handler to be implemented, event arguments defined in child class
event EventHandler<PluginEventArgs> CallbackEvent;
//event EventHandler<EventArgs> CallbackEvent;
void OnProcessStart(PluginEventArgs data);
void OnProcessEnd(PluginEventArgs data);
}
(2) custom event args
[Serializable]
public class PluginEventArgs : EventArgs
{
public string ResultMessage;
public string executingDomain;
public PluginEventArgs(string resultMessage = "")
{
// default empty values allows us to send back default event response
this.ResultMessage = resultMessage;
this.executingDomain = AppDomain.CurrentDomain.FriendlyName;
}
}
(3) example plugin class implementation
[Serializable]
public class Plugin_1 : IPlugin
{
System.Timers.Timer counter;
int TimerInterval;
string PluginName = "My plugin";
public string Name()
{
return "CMD";
}
public bool Start()
{
OnStart(new PluginEventArgs());
RunProcess();
return true;
}
// OnTimer event, process start raised, sleep to simulate doing some work, then process end raised
public void OnCounterElapsed(Object sender, EventArgs e)
{
OnProcessStart(new PluginEventArgs());
OnProcessEnd(new PluginEventArgs());
Stop();
}
public bool Stop()
{
// simulate waiting for process to finish whatever its doing....
if (counter != null)
{
counter.Stop();
OnStop(new PluginEventArgs());
}
return true;
}
public string RunProcess()
{
TimerInterval = 2000;
if (counter == null){
counter = new System.Timers.Timer(TimerInterval);
}
else {
counter.Stop();
counter.Interval = TimerInterval;
}
counter.Elapsed += OnCounterElapsed;
counter.Start();
return "";
}
public event EventHandler<PluginEventArgs> CallbackEvent;
void OnCallback(PluginEventArgs e)
{
if (CallbackEvent != null)
{
CallbackEvent(this, e);
}
}
public void OnProcessStart(PluginEventArgs Data)
{
OnCallback(new PluginEventArgs(Data.executingDomain + " - " + PluginName + " started"));
}
public void OnProcessEnd(PluginEventArgs Data)
{
OnCallback(new PluginEventArgs(Data.executingDomain + " - " + PluginName + " ended"));
}
(4) I have a plugin manager that loads/unloads
public bool LoadPlugin()
{
try
{
Domain_Command = AppDomain.CreateDomain("Second_domain");
command_loader = (ProxyLoader)Domain_Command.CreateInstanceAndUnwrap("PluginMgr", "PluginMgr.Method");
Plugins.AddPlugin(command_loader.LoadAndExecute("APluginName", Plugins.ProxyLoader_RaiseCallbackEvent), SomePluginType, false);
return true;
}
catch (Exception ex)
{
string message = ex.Message;
return false;
}
}
(5) my "ProxyLoader" to load the plugin into separate AppDomain
public class ProxyLoader : MarshalByRefObject
{
public AssemblyInstanceInfo LoadAndExecute(string assemblyName, EventHandler<PluginContract.PluginEventArgs> proxyLoader_RaiseCallbackEvent)
{
AssemblyInstanceInfo AInfo = new AssemblyInstanceInfo();
//nb: this AppDomain.CurrentDomain is in its own context / different from the caller app domain?
Assembly pluginAssembly = AppDomain.CurrentDomain.Load(assemblyName);
foreach (Type type in pluginAssembly.GetTypes())
{
if (type.GetInterface("IPlugin") != null)
{
object instance = Activator.CreateInstance(type, null, null);
AInfo.ObjectInstance = instance;
string s = ((PluginContract.IPlugin)instance).RunProcess(); // main procedure
AInfo.ASM = pluginAssembly;
((PluginContract.IPlugin)instance).CallbackEvent += proxyLoader_RaiseCallbackEvent;
((PluginContract.IPlugin)instance).Start();
instance = null;
}
}
return AInfo;
}
}
(6) I have a callback this plugs into
public event EventHandler<PluginContract.PluginEventArgs> Callback;
void OnCallback(PluginContract.PluginEventArgs e)
{
if (Callback != null)
{
Callback(this, e);
}
}
(7) called by (referenced in ProxyLoader when load the assembly)
public void ProxyLoader_RaiseCallbackEvent(object source, PluginContract.PluginEventArgs e)
{
OnCallback(new PluginContract.PluginEventArgs(str));
}
I'm getting the above error. I saw other similar posts on this error but I don't seem to find where the problem is. I changed my class type to static and the error resists. This is the entire code to my main form class:
I Edited my class. This is how it looks like now and the error has changed to:Missing partial modifier on declaration of type 'PhoneFind.frmMain'; another partial declaration of this type exists.
namespace PhoneFind
{
public partial class frmMain : Form
{
// the path to the temporary file to store the webresponse
String path = "c:\\Temp\\webresponse.txt";
public frmMain()
{
InitializeComponent();
comboSelectSearchEngine.SelectedIndex = 0;
ofdPhones.Filter = "txt files (*.txt)|*.txt";
listbxMessages.Items.Clear();
addItemToListBox(listbxMessages, "Welcome to ActionBase Phone Find!");
addItemToListBox(listbxMessages, "Select the file containing the numbers.");
radioNumbers.Checked = true;
}
private void frmMain_Load(object sender, EventArgs e)
{
}
private void btnLoadFile_Click(object sender, EventArgs e)
{
if (ofdPhones.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
txtFile.Text = ofdPhones.FileName;
// Read the file line by line and add the numbers to the numbers listbox.
listbxNumbers.Items.Clear();
String line;
int numbersCounter = 0;
System.IO.StreamReader file = new System.IO.StreamReader(ofdPhones.FileName);
while ((line = file.ReadLine()) != null)
{
listbxNumbers.Items.Add(line.Trim());
numbersCounter++;
}
addItemToListBox(listbxMessages, ofdPhones.FileName + " loaded.");
addItemToListBox(listbxMessages, numbersCounter + " records found in the file.");
}
}
// add item to the listbox and move scroll to the end of the listbox for the latest messages to be visibile to the viewer
private void addItemToListBox(ListBox listbox, String item)
{
listbox.Items.Add(item);
listbox.SelectedIndex = (listbox.Items.Count - 1);
}
private void radioNumbers_CheckedChanged(object sender, EventArgs e)
{
if (!radioNumbers.Checked)
{
grpbxNumbers.Text = "Names and Addresses";
}
else
{
grpbxNumbers.Text = "Numbers";
}
}
private void btnRun_Click(object sender, EventArgs e)
{
if (listbxNumbers.Items.Count == 0)
{
MessageBox.Show("No records have been loaded." + "\n" + "Use the browse button to load records.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (!CheckForInternetConnection())
{
MessageBox.Show("No internet connection.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
String response;
switch (comboSelectSearchEngine.SelectedIndex)
{
case 0: // Hitta.se
foreach (String item in listbxNumbers.Items)
{
WebRequestObj request = new WebRequestObj(item, "hitta");
response = request.sendRequest();
if (response.Equals("Error"))
{
addItemToListBox(listbxMessages, "Error sending '" + item + "' to the server.");
}
else
{
//create a temporary file to work on the response
StreamWriter sw;
if (!File.Exists(path)) {
sw = File.CreateText(path);
}
try
{
File.WriteAllText(path, String.Empty); // clear the content of the file
sw = File.AppendText(path);
sw.WriteLine(response);
sw.Flush();
sw.Close();
String s = findResultType(path);
MessageBox.Show(s);
}
catch (IOException ioe)
{
MessageBox.Show(ioe.Message,"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
break;
}
}
}
public bool CheckForInternetConnection()
{
try
{
using (var client = new System.Net.WebClient())
using (var stream = client.OpenRead("http://www.google.com"))
{
return true;
}
}
catch
{
return false;
}
}
/*
* findResultType
* gets the web response and finds out if the matching result of the search termis one of the following:
* 1. one person (oneperson)
* 2. one company (onecompany)
* 3. more than one person, no company (manypersonnocompany)
* 4. no person, more than one company (nopersonmanycompany)
* 5. more than one person, more than one company (manypersonmanycompany)
* 6. no person, no company (nopersonnocompany)
*/
public String findResultType(String reponsepath)
{
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.Load(reponsepath);
List<String> itemList = new List<String>();
IEnumerable<String> v = null;
var item = doc.DocumentNode.SelectNodes("//body[#id='person']");
if (item != null)
{
v = item.Select(p => p.InnerText);
itemList = v.ToList();
if (itemList.Count == 1)
return "oneperson";
}
else
{
item = doc.DocumentNode.SelectNodes("//body[#id='company']");
if (item != null)
{
v = item.Select(p => p.InnerText);
itemList = v.ToList();
if (itemList.Count == 1)
return "onecompany";
}
}
//for (int i = 0; i < itemList.Count; i++)
//{
// MessageBox.Show(itemList[i]);
// //console.writeline(itemlist[i]);
//}
//console.writeline(itemlist.count + " results found.");
return "";
}
}
}
This is your first problem:
public static class frmMain : Form
You can't derive a static class from another class. A static class always implicitly derives from Object. You also can't have non-static members in static classes.
So basically, you can't put your extension method in your frmMain class (which should be renamed to follow .NET naming conventions and to be somewhat more descriptive at the same time). Why did you want to put the extension method in that class anyway?
I can't even see an extension method in the code you've posted - did you remove it? Is it in some other class you haven't posted?
Fundamentally, I think you need to take a step back. It sounds like you've reacted to the compiler error message without really understanding it fully. Read up on extension methods, read up on what static classes are, until you really understand why you don't want your form class to be static, and why you do want to make the class that contains your extension method static.
A static class cannot have public constructors. Moreover, static forms are pointless, as you need an instance of them to show.
If you have an extension method (I don't see any...), you have to move it into a separate static class which you cannot create instances of:
static class MyExtensions
{
public static void Extend(this Object o) {}
}
You should always put your extension methods in a separate static class.
You can't fix the problem by simply marking a class as static. That changes the entire meaning of the class. Instead, create a new class for just extension methods.
For example:
public static class Extensions
{
public void SomeExtension(this object arg)
{
...
}
}