Using exception to inform about delegate not set - bad design? - c#

I've made a document class that downloads and reads the text in it. The smart thing is that it only downloads and reads the text in the document when or if it's needed. By using the Text property it'll try to read the document, if it hasn't been downloaded it'll download it then read it.
It's very nice. However I've noticed my use of Exceptions leads to some funky code. See below.
Document class
public delegate byte[] DownloadBinaryDelegate(IDocument document);
public delegate string TextReaderDelegate(IDocument document);
public class Document
{
public DownloadBinaryDelegate DownloadBinaryDelegate { private get; set; }
public TextReaderDelegate TextReaderDelegate { private get; set; }
private bool _binaryIsSet;
private byte[] _binary;
public byte[] Binary
{
get
{
if (_binaryIsSet)
return _binary;
if (DownloadBinaryDelegate == null)
throw new NullReferenceException("No delegate attached to DownloadBinaryDelegate.");
Binary = DownloadBinaryDelegate(this);
DownloadBinaryDelegate = null; // unhock delegate as it's no longer needed.
return _binary;
}
set
{
if (_binaryIsSet)
return;
_binary = value;
_binaryIsSet = true;
}
}
private bool _textIsSet;
private string _text;
public string Text
{
get
{
if (_textIsSet)
return _text;
if (TextReaderDelegate == null)
throw new NullReferenceException("No delegate attached to TextReaderDelegate.");
Text = TextReaderDelegate(this); // this delegate will call Binary and return the translated text.
TextReaderDelegate = null; // unhock delegate as it's no longer needed.
return _text;
}
set
{
if (_textIsSet)
return;
_text = value;
_textIsSet = true;
}
}
The Problem
What I wrote in the first place.
if (document.Text == null) // text is not set
{
if (document.Binary == null) // binary has not been downloaded
document.DownloadBinaryDelegate = Util.DownloadDocument;
document.TextReaderDelegate = Util.ReadDocument;
}
Totally forgetting that the Text property throws an exception.
So I have to write something like this, which is a bit funky code.
// check if text has already been read and set
try
{
var isTextSet = document.Text == null;
}
catch (NullReferenceException)
{
document.DownloadBinaryDelegate = Util.DownloadDocument;
document.TextReaderDelegate = Util.ReadDocument;
}
I hope you can see what I mean.
So my question, is this a bad design? How would you have done it? Keep in mind I would still like the current functionality.

Lazy initialization is already baked into the .NET framework. I would suggest re-implementing your class using Lazy<T>.
To answer your specific question, it sounds like your class will always require the Binary and Text delegates, so I would make them required parameters to the constructor.

I was unable to use Lazy as I'm using delegate(this) which is not allowed.
So I ended up using the fine answer from here: What exception type to use when a property cannot be null?
public class Document
{
private DownloadBinaryDelegate _downloadBinaryDelegate;
public void SetDownloadBinaryDelegate(DownloadBinaryDelegate downloadBinary)
{
if (downloadBinary == null)
throw new ArgumentNullException("downloadBinary");
_downloadBinaryDelegate = downloadBinary;
}
private TextReaderDelegate _textReaderDelegate;
public void SetTextReaderDelegate(TextReaderDelegate readerDelegate)
{
if (readerDelegate == null)
throw new ArgumentNullException("readerDelegate");
_textReaderDelegate = readerDelegate;
}
private bool _binaryIsSet;
private byte[] _bytes;
public void SetBinary(byte[] bytes, bool forceOverwrite = false)
{
if (_binaryIsSet && !forceOverwrite)
return;
_bytes = bytes;
_binaryIsSet = true;
}
public byte[] GetBinary()
{
if (_binaryIsSet)
return _bytes;
if (_downloadBinaryDelegate == null)
throw new InvalidOperationException("No delegate attached to DownloadBinaryDelegate. Use SetDownloadBinaryDelegate.");
SetBinary(_downloadBinaryDelegate(this));
_downloadBinaryDelegate = null; // unhock delegate as it's no longer needed.
return _bytes;
}
public bool TryGetBinary(out byte[] bytes)
{
if (_binaryIsSet)
{
bytes = _bytes;
return true;
}
if (_downloadBinaryDelegate != null)
{
bytes = GetBinary(); // is this legit?
return true;
}
bytes = null;
return false;
}
private bool _textIsSet;
private string _text;
public void SetText(string text, bool forceOverwrite = false)
{
if (_textIsSet && !forceOverwrite)
return;
_text = text;
_textIsSet = true;
}
public string GetText()
{
if (_textIsSet)
return _text;
if (_textReaderDelegate == null)
throw new InvalidOperationException("No delegate attached to TextReaderDelegate. Use SetTextReaderDelegate.");
SetText(_textReaderDelegate(this)); // this delegate will call Binary and return the read text.
_textReaderDelegate = null; // unhock delegate as it's no longer needed.
return _text;
}
public bool TryGetText(out string text)
{
byte[] bytes;
if (!TryGetBinary(out bytes))
{
text = null;
return false;
}
if (_textIsSet)
{
text = _text;
return true;
}
if (_textReaderDelegate != null)
{
text = GetText(); // is this legit?
return true;
}
text = null;
return false;
}
}

Related

I have an int[] member in a class that reassings value to every instance of the class with each new declaration

I have a class for saving interactions in a game, when a person reacts takes values from the status of the game to create a new instance of this class, then send it, this is fine, the problem is when I try to get the value at the end of the level, then every instance of the class has the same value for that property with coincides with the last object declared of that class
public class reactionOrOmission
{
public bool reacted
{
get { return _reacted; }
set { _reacted = value; }
}
public float reactionTime
{
get { return _reactionTime; }
set { _reactionTime = value; }
}
public bool correct
{
get { return _correct; }
set { _correct = value; }
}
public int[] flagType
{
get { return _flagType; }
set { _flagType = value; }
}
public float generalTime
{
get { return _generalTime; }
set { _generalTime = value; }
}
public string focus
{
get
{
return _focus != null ? _focus : "non_focusable";
}
set { _focus = value; }
}
private bool _reacted;
private float _reactionTime;
private bool _correct;
private int[] _flagType;
private float _generalTime;
private string _focus;
public reactionOrOmission(bool Reacted, float ReactionTime, bool Correct, int[] FlagType, float GeneralTime)
{
reacted = Reacted;
reactionTime = ReactionTime;
correct = Correct;
flagType = FlagType;
generalTime = GeneralTime;
if (Tobii.Gaming.TobiiAPI.GetFocusedObject() == null)
{
focus = "non_focusable";
}
else
{
///nonimportant///
}
}
}
Thought it may have been something relating to an integer array but i have tried arrayList and list and the same happens.
I think your class is correct but you use incorrect instances or maybe incorrect usage after create instances...I ran your class and set 4 different instance , each instance has different values.
so your usage of class is incorrect!

How do I reference a variable from one class to another?

I'm new to C# and I can't figure out how to reference the value of a variable from one class to another.
It's set to when the button is pressed, it'll take the text in the text box and sets that as "alphaask". Then it instances "alphaanswer" which would tell the label to change its text.
"alphaanswer" will take the value "alphaQuest" and see if its equal to "bob" which then would change the label.
ALL I want to know how to set the value of "alphaQuest" from the value of "alphaask" so the string can check it with "alphaanswer"
public partial class QuestionTab : Form
{
public string alphaask = "null";
public void button1_Click(object sender, EventArgs e)
{
// alphabutton
// Checks if something is in textbox then says bool is true
bool asked = false;
if(textBoxAlpha.Text != "")
{
alphaask = textBoxAlpha.Text;
asked = true;
}
if(asked==true)
{
// If bool is true than instance auxy
var instance = new Alpha();
instance.alphaanswer();
}
}
}
public class Alpha
{
string alphaQuest = // <-- I want to make alphaQuest equal to alphaask
alphaanswer();
public void alphaanswer()
{
if (alphaanswer == bob)
{
//change text in label1
}
}
}
Do these changes
public partial class QuestionTab : Form
{
public string alphaask = "null";
public void button1_Click(object sender, EventArgs e)
{
bool asked = false;
if(textBoxAlpha.Text != "")
{
alphaask = textBoxAlpha.Text;
asked = true;
}
if(asked==true)
{
// If bool is true than instance auxy
var instance = new Alpha();
instance.alphaAnswer(alphaask);
//Here you are sending the current value of alphaAsk to the alphaanswer method.
}
}
}
public class Alpha
{
public void alphaAnswer(string alphaAnswer) //This string receives the value you sent
{
if (alphaAnswer == "bob")
{
//change text in label1
}
}
}
make a contractor in class Alpha with String parameter
public Alpha(String value)
{
}
then when you call it
var instance = new Alpha(alphaask);
instance.show();

How to replace a specific string using method?

I have problem with my code , I want to replace specific string to a new one but it doesn't work
public void InsertYahoo(TextBox sender)
{
if (IsGmail(sender))
{
ReplaceGmail(sender);
}
else if(IsYahoo(sender))
{
return;
}
else
{
sender.Text +="#yahoo.com";
}
}
public bool IsYahoo(TextBox sender)
{
if (sender.Text.Contains("#yahoo.com")
{
return true;
}
else
{
return false;
}
}
public bool IsGmail(TextBox sender)
{
if (sender.Text.Contains("#gmail.com")
{
return true;
}
else
{
return false;
}
}
public void ReplaceGmail(TextBox sender)
{
sender.Text.Replace("#gmail.com, "#yahoo.com");
}
This code what i tried , so any suggestions?
Also I tried to get the index of #gmail.com and remove it but it did not work neither
Strings are immutable, so every method in the String class does not modify the current instance but returns a new one. You have to assign this to the original variable:
sender.Text = sender.Text.Replace("#gmail.com,"#yahoo.com");
If you are interested in why strings are immutable: Why .NET String is immutable?
Something like this:
//DONE: we should check for null
//DONE: it's Yahoo if it ends on #yahoo.com (not contains)
public static bool IsYahoo(TextBox sender) =>
sender != null &&
sender.Text.TrimEnd().EndsWith("#yahoo.com", StringComparison.OrdinalIgnoreCase);
public static bool IsGmail(TextBox sender) =>
sender != null &&
sender.Text.TrimEnd().EndsWith("#gmail.com", StringComparison.OrdinalIgnoreCase);
public static void InsertYahoo(TextBox sender) {
if (null == sender)
throw new ArgumentNullException(nameof(sender));
if (IsYahoo(sender))
return;
// Uncomment, In case you want to change gmail only
//if (!IsGmail(sender))
// return;
// If we have an eMail like bla-bla-bla#somewhere
int p = sender.Text.LastIndexOf('#');
// ... we change somewhere to yahoo.com
if (p > 0)
sender.Text = sender.Text.Substring(0, p) + "#yahoo.com";
}

Change data in Class when List<sub-class> data is changed

I have a class STimer that has a List in it. The serviceDetail is monitored on a timer very often, and rarely changes, but when it does change I want to get the fact that the data changed without looping through the list due to processing power. Maybe this is a duplicate question that I just don't know how to search for it, but I have been trying. Here is a code Sample:
class STimers
{
public class ServiceDetail
{
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
<-- Update STimers._dataChanged to true -->
}
}
}
}
public List<ServiceDetail> _serviceMonitors = new List<ServiceDetail>();
public bool _dataChanged = false;
}
I could do a .Find on the list to return all of the _serviceMonitors._runningStateChanged=true, but that seems like a lot of work parsing the List every time the timer fires, when likely only 1 out of 1,000 loops will actually have a change.
Is this even possible, or do I need to move the check for changes out of the class?
You could get this by adding an event to your ServiceDetail class
public class ServiceDetail
{
public event EventHandler<ListChangedEventArgs> ListChanged;
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
private void OnListChanged(ListChangedEventArgs e){
if (ListChanged != null) ListChanged(this, e);
}
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
OnListChanged(new ListChangedEventArgs(this));
<-- Update STimers._dataChanged to true -->
}
}
}
}
And define your ListChangedEventArgs class like this
public class ListChangedEventArgs:EventArgs
{
public ServiceDetail serviceDetail { get; set; }
public ListChangedEventArgs(ServiceDetail s)
{
serviceDetail = s;
}
}
And then register to the event for each servicedetail added to the list
s.ListChanged += (sender, args) => YourFunction();
Hope it helps

Serialization and deserialization from clipboard (copy/paste)

I'm trying to add copy/paste feature to my WPF application. I have a DataGrid which I allowed to select an entire row. A row is an object of type AcquisitionParameters. The selection is ok, the copy to clipboard too. After copying to clipboard, I verify if the data has been well serialized and it is the case. But when I try to retrieve the object from clipboard, I am not able to deserialize it on its original format, but in a CSV format.
private void dataGridAcquisitions_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
System.Windows.Clipboard.Clear();
DataFormat format = System.Windows.DataFormats.GetDataFormat(typeof(AcquisitionParameters).FullName);
System.Windows.IDataObject dataObj = new System.Windows.DataObject();
dataObj.SetData(format.Name, (AcquisitionParameters)e.Item, false);
System.Windows.Clipboard.SetDataObject(dataObj, true);
bool ispresent = dataObj.GetDataPresent(format.Name); // ok
AcquisitionParameters parameters = dataObj.GetData(format.Name) as AcquisitionParameters; //ok
if(parameters != null && ispresent)
{
//enter here
}
}
private void dataGridAcquisitions_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.V && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
System.Windows.IDataObject dataObj = System.Windows.Clipboard.GetDataObject(); //type of AcquisitionParameters not available, only HTML, CSV, etc.
string format = typeof(AcquisitionParameters).FullName;
if(dataObj.GetDataPresent(format)) //false
{
AcquisitionParameters parameters = dataObj.GetData(format) as AcquisitionParameters;
if(parameters != null)
{
}
}
}
}
And the declaration of my AcquisitionParameters class
namespace App
{
[Serializable]
public class AcquisitionParameters
{
private double pulse;
public double Pulse
{
get { return pulse; }
set { pulse = value; }
}
private double range;
public double Range
{
get { return range; }
set { range = value; }
}
private double offset;
public double Offset
{
get { return offset; }
set { offset = value; }
}
}
}
Try accessing the data from the Clipboard like this:
object data = Clipboard.GetData("AcquisitionParameters");
if (data != null) return (AcquisitionParameters)data;
return new AcquisitionParameters();
UPDATE >>>
If that doesn't help, then try setting the data like this:
DataObject dataObject = new DataObject();
dataObject.SetData("AcquisitionParameters", (AcquisitionParameters)e.Item, false);
Clipboard.SetDataObject(dataObject);

Categories

Resources