|
That way has an advantage, and it's pretty major - you can chain things:
DoSomething(myobj).DoSomethingElse(); But it's often nicer to see it the otehr way round:
myObj.DoSomething().DoSomethingElse(); For example, if I want today's date (without the time), but one year and one day in the future:
DateTime expiry = DateTime.Now.Date.AddYears(1).AddDays(1); If they didn't return values then it gets messy to work out what is happening:
DateTime expiry = DateTime.Now;
expiry.Date(out expiry);
expiry.AddYears(1, out expiry);
expiry.AddDays(1, out expiry);
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Wow that is an exceptional response. I really appreciate that. Now I have more confidence in that code so I can look at other things that might be causing my problems with a program I'm working on. I was just going to pull all that stuff out and rewrite it. You saved me from that.
|
|
|
|
|
Keep in mind the nuance in Griff's example that DateTime is a struct, not a class, which is why it must return a new instance.
DateTime struct
Any changes it makes internally as a result of a method call will not be seen by the caller, as the instance is passed by value.
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
If you really want method chaining a la functional programming, .NET Extension Methods (C# >= 3.0) are very useful (example code below).
The "virtue" I see in using 'ref and 'out parameters is that your code is forced to "make visible," for both calling and called code, what is going on. imho, visible == maintainable.
Example of extension methods:
public class SomeObj
{
public string SomeParam { get; set; }
public int Id { get; }
public Dictionary<string, DateTime> ParamLog { get; set; }
public SomeObj(string sparam, int id)
{
SomeParam = sparam;
Id = id;
ParamLog = new Dictionary<string, DateTime> {{sparam, DateTime.Now}};
}
}
public static class SomeObjExtensions
{
public static SomeObj SetSomeParam(this SomeObj obj, string newsparam)
{
if (obj.SomeParam.Equals(newsparam)) return obj;
obj.SomeParam = newsparam;
obj.ParamLog.Add(newsparam, DateTime.Now);
return obj;
}
public static SomeObj Clone(this SomeObj obj, string newsparam)
{
var newobj = new SomeObj(newsparam, obj.Id);
newobj.ParamLog = new Dictionary<string, DateTime>(obj.ParamLog);
return newobj;
}
} Note: for issues in cloning Dictionaries see: [^]
«While I complain of being able to see only a shadow of the past, I may be insensitive to reality as it is now, since I'm not at a stage of development where I'm capable of seeing it.» Claude Levi-Strauss (Tristes Tropiques, 1955)
|
|
|
|
|
Hi Bill, That was my initial thought about ref and out being more visible. For someone who's trying to keep all of these things straight, that kind of hint is important. I'm definitely a big fan of extension methods and have written several that I uses often. Of course some of them do things that can already be done with Null coalescing operator and null conditional operators, but I haven't used those in practice and so I forget about them... and LINQ, geez, I feel like I'm stuck in the dark ages until I get up to speed with those things.
The lack of surety in programming is part of the reason software is fragile.
|
|
|
|
|
@Rick_Bishop @Marc-Clifton
Hi Rick,
If you do get into the "functional programming" thing, as in F#, you will have reasons, imho, to think about the issue of "immutability" ... F# enforces that strictly.
Marc Clifton's recent articles here that demonstrate FP approaches with C# are very interesting.
I am inclined, now, to do without 'ref and 'out, and make my classes as immutable as possible using 'get only Properties, etc. You can see this in the second extension method above named 'Clone.
With a very complex Class, copying all the field/property values into a new object might be very expensive; in that case, is the Class "more immutable" if there is a specific method that modifies one or more class instance members values ? Not yet sure on that one.
In any case, I think it imperative to think carefully about when, how, and where, any Class instance may be modified.
cheers, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
BillWoodruff wrote: I think it imperative to think carefully about when, how, and where, any Class instance may be modified.
How very functional of you.
BillWoodruff wrote: I am inclined, now, to do without 'ref and 'out,
C# 7's tuple return all but eliminates the need for out. Of all the new additions to the language, it is currently my favorite.
BillWoodruff wrote: to think about the issue of "immutability" ... F# enforces that strictly.
Actually not quite, the mutable keyword explicitly declares that the field can be changed, which is particularly useful dealing with .NET classes in F#.
BillWoodruff wrote: With a very complex Class,
The interesting thing I found about F# is that it one usually doesn't write complex classes. Furthermore, a function call rarely needs to clone an object or mutate a field, instead, the function returns a completely new type with the result. This is probably the most dramatic change in thinking when using FP languages -- a function operates on a type and returns a different type with the result of the operation.
A simple example is a type that contains a vehicle make, model, and year. In C#, we might implement the class with a Bitmap for a stock image of that vehicle and method called GetStockImage() that mutates the container class by assigning the bitmap.
In F#, you might simply return a different type containing the image and let the caller combine the make/model/year with the image into a new type. Or, the call to GetStockImage could do that, returning that new type.
So in C#, you would have implemented one "type" as a class. In F#, you would have implemented two, possible three types. The beauty of the F# approach is that the function ShowStockImage is guaranteed (well, in theory) of being passed only a type instance that actually has an image that's been initialized. No "if (image == null) throw Exception" necessary!
Ideally, in F#, you would also have real types for make, model, and year, not just strings and ints!
I hope that made sense!
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
Hi Marc, That makes a whole lotta sense, as your thoughts usually do
I also, am enjoying C#'s new Tuple facility.
I can't quite see how F# would, inherently, remove a need to have complex POCOS: if a POCO gotta have a lotta fields or properties ... ? Perhaps you are suggesting using Tuples with multiple Types like a property-bag ?
The mind boggles at thhe thought of this:
public class POCOLocoTuple
{
public POCOLocoTuple()
{
}
public POCOLocoTuple(string name, int age)
{
PBag = (Guid.NewGuid(), name, age);
}
private (Guid Id, string Name, int Age) PBag { set; get; }
public string Name
{
set
{
PBag = (PBag.Id, value, PBag.Age);
}
}
public int Age
{
set
{
PBag = (PBag.Id, PBag.Name, value);
}
}
public POCOLocoTuple Clone(bool makenewguid = true)
{
Guid guid = makenewguid ? Guid.NewGuid() : PBag.Id;
POCOLocoTuple newpocoloco = new POCOLocoTuple();
newpocoloco.PBag = (guid, PBag.Name, PBag.Age);
return newpocoloco;
}
} cheers, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
modified 6-Jan-18 16:48pm.
|
|
|
|
|
BillWoodruff wrote: Perhaps you are suggesting using Tuples with multiple Types like a property-bag ?
Not really, but keep in mind that my experience with FP is limited.
BillWoodruff wrote: I can't quite see how F# would, inherently, remove a need to have complex POCOS: if a POCO gotta have a lotta fields or properties
One of the differences between imperative and functional programming is the concept of currying and partial application. Currying - you call a function with only the first subset of parameters, partial application - you create a function that itself calls a function requiring additional parameters. In either case, what you get back from currying is a function (you can think of it as "waiting for the rest of the parameters") or with partial application, you've defined a function that is "waiting for parameters").
Given that capability, the need for POCO with lots of properties (or fields in FP parlance) often becomes unnecessary. A simple example:
class BirthDate
{
public int Month {get;set;}
public int Day {get;set;}
public int Year {get;set;}
}
class Horoscope
{
public string GetHoroscope(BirthDate date);
public string GetHoroscope(int y, int m, int d);
}
Here, I've used BirthDate as a container for 3 fields, or without the container, as three parameters. In either case, calling GetHoroscope requires that all three parameters are known before making the call, so they're wrapped in a container or perhaps in local variables.
In F#, you don't need that. You can do this:
let GetHoroscope y m d = [implementation details]
Let's say you have a simple text-based UI (pseudo-codish):
let curried1 = GetHoroscope PromptForYear
let curried2 = curried1 PromptForMonth
let text = curried2 PromptForDay
Notice a few things here. First of all, for y, m, and d parameters, I'm passing in a function "PromptFor...", not an integer variable.
Secondly, curried1 and curried2 are functions expecting day and year, and year, respectively.
Where are the variables, fields, properties? They exist ONLY in [implementation details] -- the caller never creates a variable, never creates a container, etc.
So, one of the reasons POCO with lots of fields is not necessary is that those fields, in FP, are actually implemented as functions in FP, called as needed. While the example can be written imperatively:
GetHoroscope(PromptForYear(), PromptForMonth(), PromptForDay());
The problem here, with the imperative code, is that "how" you're getting those values is hard-coded -- there's no easy way to curry the C# method GetHoroscope . Even more interestingly, curried1 and curried2 can be returned as functions!
Currying, and partial function application, in C#, becomes a mess.
So one has to get out of the mindset of thinking of data, not as values but instead data as functions. That is NOT easy to do!
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
Hi Marc, Thanks for this virtual banquet of food for thought !
Clearly, I am yet unclear on the "deeper state" of the FP paradigm, but, it is very interesting.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Hi @BillWoodruff, Funny you'd mention immutability. I've been reading about immutable properties and started using them in my project. These are the properties with only getters and no setters, that can only be set from the constructor. It turns out that many of my objects are like that because I tend to use a lot of classes just to hold data rather than having to make additional database calls, especially inside a loop. I hope this is the correct usage. If not, I've got a lot of searching and replacing to do.
The lack of surety in programming is part of the reason software is fragile.
|
|
|
|
|
Except for method chaining, why would pass the same object back to the caller? Since the object is passed by reference, the caller's reference is updated. So, in your example:
SomeObj myobj = new SomeObj ( someparam = "Some Value" );
myobj = DoSomething( myobj );
the assignment is unnecessary and potentially confusing -- the reader of the code might wonder if myobj is not the same instance that is returned from the call DoSomething in which you passed in myobj
Of course, if SomeObj is struct, then it's passed by value, and yeah, you have to return the "value" for the caller to see the change.
I must say, I'm amused by all this because your question hit the bullseye of how messy method calling can be.
As a digression, think of higher level method as a workflow:
var stuffWorkflowNeedsToKnow = InitializeStuff();
DoStep1(stuffWorkflowNeedsToKnow);
DoStep2(stuffWorkflowNeedsToKnow);
DoStep3(stuffWorkflowNeedsToKnow);
And a further digression: Class-less Coding - Minimalist C# and Why F# and Function Programming Has Some Advantages
Latest Article - Code Review - What You Can Learn From a Single Line of Code
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
Are there any C# programmer forums that are more forgiving towards people who are fairly new to the language?
Where it's ok to ask questions that haven't been thoroughly researched?
Where you don't get downvoted because a question has been asked before (because every question has)?
A resource where one might even get assistance with asking the question itself?
Often times, questions are difficult to articulate due to the learner's lack of experience and unfamiliarity with the vocabulary.
I can continue doing this on my own, but the learning curve could really improve if I could ask a few questions that might seem ridiculously easy to an experienced programmer. Entire technologies has been deprecated since C# was invented, yet it's all still online as recommended best practices.
|
|
|
|
|
I would say that you can ask every question in this Forum.
Of course you often would get a Link which could answer your question.
My experience with this forum is : as better you specify your issue as better the answer would be - independant if it's posted elsewhere
What you should avoid is any question which could understood as "please write the code for me" or "I don't want to use the debugger" or "what the hell is Google" or could be interpreted as homework from school ...
|
|
|
|
|
|
If only I knew someone who'd written such an article[^]. It's over 10 years old and not a lot has changed in the intervening years.
This space for rent
|
|
|
|
|
Not the one I was thinking of, I don't think - but it's a damn good 'un!
Someone had a link in their sig, IIRC - but for the life of me I can't recall who...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
It was Dave Kreskowiak who had the links. First should have lead to this[^] article. The second one leads to this[^].
This space for rent
|
|
|
|
|
Dats de bunny!
He removed the link, which is why I got confused, probably ... that or the inevitable rise of senility.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
One of the links went dead so it was removed. Actually, I think it was either you or Pete that told me of it!
I'll gladly put them back... after I wake up.
System.ItDidntWorkException: Something didn't work as expected.
-- said no compiler, ever.
C# - How to debug code[ ^].
Seriously, go read this article.
Dave Kreskowiak
|
|
|
|
|
I think I mentioned the plural, but not the original dead link.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I put the links back in, with only 4 characters to spare. We need a bigger signature space.
|
|
|
|
|
I've got 360 spare at the mo, you can have 150 if you need them?
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Awsome! Thank you! I'll just ask Chris to increase my credit limit.
|
|
|
|
|
Sorry, it's 1:30am here and I've been working all day, so I may have missed something in that article that discusses alternatives to CodeProject. I'll look again though.
|
|
|
|
|